【LightOJ】Assassin`s Creed (II) (缩点,传递闭包,二分图匹配,最小路径覆盖)
来源:互联网 发布:淘宝网怎么打不开了 编辑:程序博客网 时间:2024/06/06 14:18
题目链接:
http://acm.bnu.edu.cn/v3/problem_show.php?pid=23628
这道题是一道图论的综合题。题意较简单,如果对图论部分算法较为熟悉,那么很快便能找到清晰的解题思路。而且这道题中涉及了多种算法,对新手来说这是个很好的训练自己,提升自己的题目。
这是一个有向图A(可能有环)的最小路径覆盖问题。首先,利用【tarjan算法】缩点,得到一个DAG图B,然后用算一次图B的传递闭包,因为下一步利用二分图匹配去算最小路径覆盖的时候,所需要的边不仅仅是图B原来存在的边,而是B图的传递闭包中所有的边,具体原因看这里:
http://www.cnblogs.com/ka200812/archive/2011/07/31/2122641.html
算传递闭包的方法有很多,比如:
1 Floyd-Warshall算法 http://www.nocow.cn/index.php/Floyd-Warshall%E7%AE%97%E6%B3%95#.E6.94.B9.E8.BF.9B.E5.92.8C.E4.BC.98.E5.8C.96
2对每个点做一次 dfs / bfs(优化)
3 对每个点做一次 spfa(优化)
因为在这道题中,顶点数为10^3,边数为10^5,时间限制是4s,所以用Floyd-Warshall算法会超时,从而只能选择用2,3两种方法,而且必须是利用边邻接表优化过的算法,否则仍然会超时。
剩下的就是利用二分图匹配来算最小路径覆盖,我使用的是【Hopcroft-Carp 算法】。
这道题我前前后后做了一个多月,主要是想尝试找出一个更简单的求最小路径覆盖的方法,但是后来发现自己想到的方法中均出现了不好处理的错误,于是最终还是选择了使用二分图匹配法,之后又发现自己使用【Floyd-Warshall算法】算传递闭包时O(n^3)的复杂度会使得程序超时,转念想到稀疏图中【spfa算法】有更好表现(复杂度为O(ke),k近似为2),最终利用更快的spfa求得传递闭包。此题终结。
最后是3488ms时间AC了,但是和其他人的代码比起来,我自己的代码明显还有更多的优化空间:
代码:
#include <cstdio>#include <cmath>#include <climits>#include <cstring>#include <string>#include <algorithm>#include <iostream>#include <vector>#include <stack>#include <queue>#include <map>#define LL long long#define db double#define pi acos(-1.0)#define pr printf#define sc scanf#define mod#define N 1005#define M 10003#define typec int // type of costusing namespace std;LL MAX_LL = 0xfffffffffffffffLL;bool c[N][N],g[N][N];//INITbool Bc[N][N];//新建图的邻接表//INITint Bin[N];//新建图中每个节点的入度//INITint DFN[N];//INIT//DFN[]数组起到了vis[]数组的作用,不用另外开vis[]数组int LOW[N];bool instack[N];//判断节点是否在栈中//INITint Belong[N];//判断节点属于哪一个分量int Index,n,m,Bn;//Bn是连通分量的个数//INITstack <int > sta;void Init(){ int i,j; while(!sta.empty()) sta.pop(); Index = Bn = 0; for(i=0; i<=n; ++i) { instack[i] = 0; DFN[i] = 0; for(j=0; j<=n; ++j) c[i][j] = Bc[i][j] = g[i][j] = 0; }}void tarjan(int now){ int i; DFN[now] = LOW[now] = ++Index; sta.push(now); instack[now] = 1;//更新now在栈中 for(i=1; i<=n; ++i) { if(!c[now][i]) continue; if(!DFN[i])//如果没访问过 { tarjan(i); LOW[now] = min(LOW[now],LOW[i]); } else if(instack[i])//如果在栈中 { LOW[now] = min(LOW[now],DFN[i]); } } if(DFN[now] == LOW[now]) { Bn++;//强连通分量增加 int t; do { t = sta.top(); sta.pop(); instack[t] = 0;//记录出栈 Belong[t] = Bn;//该节点属于哪个分量 } while(now!=t); }}const int MAXN = N;const int INF = 1 << 28;int Mx[MAXN], My[MAXN], Nx, Ny;int dx[MAXN], dy[MAXN], dis;bool vst[MAXN];bool searchP(void){ queue<int> Q; dis = INF; memset(dx, -1, sizeof(dx)); memset(dy, -1, sizeof(dy)); for (int i = 0; i < Nx; i++) if (Mx[i] == -1) { Q.push(i); dx[i] = 0; } while (!Q.empty()) { int u = Q.front(); Q.pop(); if (dx[u] > dis) break; for (int v = 0; v < Ny; v++) if (g[u][v] && dy[v] == -1) { dy[v] = dx[u]+1; if (My[v] == -1) dis = dy[v]; else { dx[My[v]] = dy[v]+1; Q.push(My[v]); } } } return dis != INF;}bool DFS(int u){ for (int v = 0; v < Ny; v++) if (!vst[v] && g[u][v] && dy[v] == dx[u]+1) { vst[v] = 1; if (My[v] != -1 && dy[v] == dis) continue; if (My[v] == -1 || DFS(My[v])) { My[v] = u; Mx[u] = v; return 1; } } return 0;}int MaxMatch(void){ int res = 0; memset(Mx, -1, sizeof(Mx)); memset(My, -1, sizeof(My)); while (searchP()) { memset(vst, 0, sizeof(vst)); for (int i = 0; i < Nx; i++) if (Mx[i] == -1 && DFS(i)) res++; } return res;}int head[N],next[M],to[M];//注意每个数组的sizeint dis1[N],len[M];bool vis[N];int en;//en是边数void add(int u,int v,int w){ to[en]=v,len[en] = w,next[en] = head[u],head[u] = en++;}void spfa(int s){ for(int i=0;i<=Bn;++i) { dis1[i] = INT_MAX; vis[i] = 0; } queue<int > q; q.push(s); dis1[s] = 0; vis[s] = 1; while(!q.empty()) { int u = q.front(); Bc[s+1][u+1] = 1; q.pop(); vis[u] = 0; for(int i=head[u];i!=-1;i=next[i]) { int v = to[i]; if(dis1[u]+len[i]<dis1[v]) { dis1[v] = dis1[u] + len[i]; if(!vis[v]) { q.push(v); vis[v] = 1; } } } }}int main(){ int T,cas=0,i,j,u,v; sc("%d",&T); while(T--) { sc("%d%d",&n,&m); Init(); while(m--) { sc("%d%d",&u,&v); c[u][v] = 1; } for(i=1; i<=n; ++i) if(!DFN[i]) tarjan(i); Nx = Ny = Bn; memset(head,-1,sizeof(head)); en = 0; for(i=1; i<=Bn; ++i) Bin[i] = 0; for(i=1; i<=n; ++i) { int x = Belong[i]; for(j=1; j<=n; ++j) { int y = Belong[j]; if(x!=y&&c[i][j]) { Bc[x][y] = 1; add(x-1,y-1,1); Bin[y]++; } } } for(i=1;i<=Bn;i++) { spfa(i-1); } for(i=1;i<=Bn;++i) { for(j=1;j<=Bn;++j) { if(Bc[i][j]&&i!=j) g[i-1][j-1] = 1; } } int res = MaxMatch(); pr("Case %d: %d\n",++cas,Bn-res); } return 0;}
- 【LightOJ】Assassin`s Creed (II) (缩点,传递闭包,二分图匹配,最小路径覆盖)
- lightoj 1429 - Assassin`s Creed (II) 【BFS预处理传递闭包 + SCC缩点 + DAG最小路径覆盖】
- 【最小路径覆盖】Assassin`s Creed (II)
- Light OJ 1429 Assassin`s Creed (II) BFS+缩点+最小路径覆盖
- LightOJ - 1429 Assassin`s Creed (II)(二分图)
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- lightoj 1429 - Assassin`s Creed (II)
- POJ 2594--Treasure Exploration【二分图 && 最小路径覆盖 && 点可以重复走 && 传递闭包】
- 二分图-最大匹配,最小路径覆盖,最小点覆盖
- HDU 3861--The King’s Problem【scc缩点构图 && 二分匹配求最小路径覆盖】
- HDU 3861 The King’s Problem (强连通分量缩点+二分图匹配最小路径覆盖)
- 二分图最大匹配,最小点覆盖,最小路径覆盖,二分图最大独立集
- Light OJ 1429 - Assassin`s Creed (II)
- 二分图的最大匹配(最小路径覆盖,最小点覆盖)
- 浅显易懂二分图-最大匹配,最小路径覆盖,最小点覆盖
- 二分图最大匹配,点的最小覆盖,最小路径覆盖
- 二分图最大匹配,点的最小覆盖,最小路径覆盖
- 二分图-最大匹配,最小路径覆盖,最小点覆盖(KM算法)
- c++拷贝构造函数、赋值运算符=重载、深拷贝与浅拷贝
- PL/SQL语法之if结构
- [NOI2010]超级钢琴 解题报告
- 数据挖掘贝叶斯(Bayes)算法java实现 带注释详解
- Android Animations动画使用详解
- 【LightOJ】Assassin`s Creed (II) (缩点,传递闭包,二分图匹配,最小路径覆盖)
- java中关于时间的计算
- 用自定义view实现的简单画图板
- python selenium使用demo
- 这事csdn网站上的一道题
- exp,imp问题
- linux硬盘分区和文件系统
- ios7 UILabel sizeWithFont方法的最新写法
- HTTP Request/Response header