【LightOJ】Assassin`s Creed (II) (缩点,传递闭包,二分图匹配,最小路径覆盖)
2015-01-17 14:10
387 查看
题目链接:
http://acm.bnu.edu.cn/v3/problem_show.php?pid=23628
这道题是一道图论的综合题。题意较简单,如果对图论部分算法较为熟悉,那么很快便能找到清晰的解题思路。而且这道题中涉及了多种算法,对新手来说这是个很好的训练自己,提升自己的题目。
这是一个有向图A(可能有环)的最小路径覆盖问题。首先,利用【tarjan算法】缩点,得到一个DAG图B,然后用算一次图B的传递闭包,因为下一步利用二分图匹配去算最小路径覆盖的时候,所需要的边不仅仅是图B原来存在的边,而是B图的传递闭包中所有的边,具体原因看这里:
/article/6002635.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求得传递闭包。此题终结。
![](http://img.blog.csdn.net/20150117143825866?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbjcyMzI4NDg5Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
最后是3488ms时间AC了,但是和其他人的代码比起来,我自己的代码明显还有更多的优化空间:
![](http://img.blog.csdn.net/20150117144012945?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbjcyMzI4NDg5Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20150117144023256?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbjcyMzI4NDg5Mw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
代码:
http://acm.bnu.edu.cn/v3/problem_show.php?pid=23628
这道题是一道图论的综合题。题意较简单,如果对图论部分算法较为熟悉,那么很快便能找到清晰的解题思路。而且这道题中涉及了多种算法,对新手来说这是个很好的训练自己,提升自己的题目。
这是一个有向图A(可能有环)的最小路径覆盖问题。首先,利用【tarjan算法】缩点,得到一个DAG图B,然后用算一次图B的传递闭包,因为下一步利用二分图匹配去算最小路径覆盖的时候,所需要的边不仅仅是图B原来存在的边,而是B图的传递闭包中所有的边,具体原因看这里:
/article/6002635.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 cost using namespace std; LL MAX_LL = 0xfffffffffffffffLL; bool c ,g ;//INIT bool Bc ;//新建图的邻接表//INIT int Bin ;//新建图中每个节点的入度//INIT int DFN ;//INIT//DFN[]数组起到了vis[]数组的作用,不用另外开vis[]数组 int LOW ; bool instack ;//判断节点是否在栈中//INIT int Belong ;//判断节点属于哪一个分量 int Index,n,m,Bn;//Bn是连通分量的个数//INIT stack <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 ,next[M],to[M];//注意每个数组的size int dis1 ,len[M]; bool vis ; 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 1429 - Assassin`s Creed (II) 【BFS预处理传递闭包 + SCC缩点 + DAG最小路径覆盖】
- LightOJ - 1429-Assassin`s Creed (II)(tarjan找环缩点+bfs+最小路径覆盖)
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- Light OJ 1429 Assassin`s Creed (II) BFS+缩点+最小路径覆盖
- Light OJ 1429 Assassin`s Creed (II) BFS+缩点+最小路径覆盖
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- 【最小路径覆盖】Assassin`s Creed (II)
- Light OJ 1406 Assassin`s Creed 减少国家DP+支撑点甚至通缩+最小路径覆盖
- poj 2594 Treasure Exploration最小路径覆盖+传递闭包
- POJ 2594 最小路径覆盖 + 传递闭包
- hdu 3861(tarjan 缩点 + 二分图匹配 求最小路径覆盖
- POJ 2594--Treasure Exploration【二分图 && 最小路径覆盖 && 点可以重复走 && 传递闭包】
- POJ-2594-Treasure Exploration-(路径相交的最小路径覆盖+闭包传递)
- K - Treasure Exploration - POJ 2594(最小路径覆盖+闭包传递)
- poj 2594 Treasure Exploration(最小路径覆盖+闭包传递)
- POJ 2594 最小路径覆盖 + 传递闭包
- HDU 3861 The King’s Problem (强连通分量缩点+二分图匹配最小路径覆盖)
- 二分图匹配 --- 最小路径覆盖
- HDU OJ 1350 Taxi Cab Scheme 【二分图匹配之最小路径覆盖】
- LA 3126 二分图匹配 最小路径覆盖