hdu 6165 FFF at Valentine (Tarjan算法,scc+dp)
2017-08-23 17:48
597 查看
题目链接:传送门
第一次遇到Tarjan算法的题。Tarjan算法讲解:传送门
引用的链接:传送门
【强连通分量】
有向图中, u可达v不一定意味着v可达u. 相互可达则属于同一个强连通分量(Strongly Connected Component, SCC)有向图和它的转置的强连通分量相同
所有SCC构成一个DAG
1、强连通图。在一个强连通图中,任意两个点都通过一定路径互相连通。比如图一是一个强连通图,而图二不是。因为没有一条路使得点4到达点1、2或3。
2、强连通分量。在一个非强连通图中极大的强连通子图就是该图的强连通分量。比如图三中子图{1,2,3,5}是一个强连通分量,子图{4}是一个强连通分量。
【Tarjan算法】
其实,tarjan算fb7c
法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的Dfn值(很绕嘴,往下看你就会明白),Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和对栈的操作,我们就可以得到该有向图的强连通分量。
1、数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。
2、堆栈:每搜索到一个点,将它压入栈顶。
3、当点p与点p’相连时,如果此时(时间为dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。
4、当点p与点p’相连时,如果此时(时间为dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。
5、每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。
6、继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
由于每个顶点只访问过一次,每条边也只访问过一次,我们就可以在O(n+m)的时间内求出有向图的强连通分量。但是,这么做的原因是什么呢?
【Tarjan算法的操作原理】
1、Tarjan算法基于定理:在任何深度优先搜索中,同一强连通分量内的所有顶点均在同一棵深度优先搜索树中。也就是说,强连通分量一定是有向图的某个深搜树子树。2、可以证明,当一个点既是强连通子图Ⅰ中的点,又是强连通子图Ⅱ中的点,则它是强连通子图Ⅰ∪Ⅱ中的点。
3、这样,我们用low值记录该点所在强连通子图对应的搜索子树的根节点的Dfn值。注意,该子树中的元素在栈中一定是相邻的,且根节点在栈中一定位于所有子树元素的最下方。
4、强连通分量是由若干个环组成的。所以,当有环形成时(也就是搜索的下一个点已在栈中),我们将这一条路径的low值统一,即这条路径上的点属于同一个强连通分量。
5、如果遍历完整个搜索树后某个点的dfn值等于low值,则它是该搜索子树的根。这时,它以上(包括它自己)一直到栈顶的所有元素组成一个强连通分量。
题目大意:有n个点,m条边,u v ,代表u可以到达v,问是否任意两点之间都有一条有向路(从a到b,或者从b到a)。
思路:模板题,Tarjan算法+scc+dp;
#include<stdio.h> #include<string.h> #include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <stack> #include <algorithm> using namespace std; const int MAXN = 1005; vector<int> g[MAXN], scc[MAXN], G[MAXN]; stack<int> s; int pre[MAXN], lowlink[MAXN], sccno[MAXN], sccnum[MAXN], dfs_clock, scc_cnt; int d[MAXN]; int n, m; int Tarjan(int u) { lowlink[u] = pre[u] = ++dfs_clock; s.push(u); for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (!pre[v]) { Tarjan(v); lowlink[u] = min(lowlink[v], lowlink[u]); } else if (!sccno[v]) lowlink[u] = min(lowlink[u], pre[v]); } if (lowlink[u] == pre[u]) { scc_cnt++; while(1) { int x = s.top(); s.pop(); sccno[x] = scc_cnt; sccnum[sccno[x]]++; if (x == u) break; } } } void find_scc() { memset(pre, 0, sizeof(pre)); memset(lowlink, 0, sizeof(lowlink)); memset(sccno, 0, sizeof(sccno)); memset(sccnum, 0, sizeof(sccnum)); dfs_clock = scc_cnt = 0; for (int i = 0; i < n; i++) if (!pre[i]) Tarjan(i); } int dp(int i) { int& ans = d[i]; if (ans > 0) return ans; ans = sccnum[i]; for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; ans = max(ans, dp(v) + sccnum[i]); } return ans; } int main() { int cas; scanf("%d", &cas); while (cas--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) g[i].clear(); int u, v; for (int i = 0; i < m; i++) { scanf("%d%d", &u, &v); u--; v--; g[u].push_back(v); } find_scc(); memset(d, -1, sizeof(d)); memset(G, 0, sizeof(G)); for (int u = 0; u < n; u++) { for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (sccno[u] != sccno[v]) G[sccno[u]].push_back(sccno[v]); } } int ans = 0; for (int i = 1; i <= scc_cnt; i++) ans = max(ans, dp(i)); if(ans==n) printf("I love you my love and our love save us!\n"); else printf("Light my fire!\n"); } return 0; }
标程:
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; struct hehe{int to,next;}e[6005]; int h[1005],cnt; int dfn[1005],low[1005],vst[1005]; int sta[500005],instack[1005]; int val[1005],top,Index,color; int map[1005][1005],rd[1005]; int T,n,m; void init() { memset(vst,0,sizeof(vst)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(instack,0,sizeof(instack)); memset(h,0,sizeof(h)); memset(map,0,sizeof(map)); memset(rd,0,sizeof(rd)); top=0;cnt=0;Index=0;color=0; } void add(int x,int y) { cnt++;e[cnt].to=y;e[cnt].next=h[x];h[x]=cnt; } void dfs(int u) { dfn[u]=low[u]=++Index; sta[++top]=u; int i,v; instack[u]=vst[u]=1; for(i=h[u];i;i=e[i].next) { v=e[i].to; if(!vst[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { color++; do { v=sta[top--]; instack[v]=0; val[v]=color; }while(v!=u); } } int q[100005]; bool solve() { int i,j,jj,y; for(i=1;i<=n;i++) if(!vst[i]) dfs(i); for(i=1;i<=n;i++) for(j=h[i];j;j=e[j].next) { y=e[j].to; if(val[i]!=val[y]) map[val[i]][val[y]]=1; } if(color==1) return false; for(i=1;i<=color;i++) for(j=1;j<=color;j++) if(map[j][i]) rd[i]++; for(i=1;i<=color;i++) { for(j=1;j<=color;j++) if(map[i][j]) break; for(jj=1;jj<=color;jj++) if(map[jj][i]) break; if(j>color&&jj>color) return true; } memset(vst,0,sizeof(vst)); q[0]=0; for(i=1;i<=color;i++) if(rd[i]==0) { q[0]++; q[q[0]]=i; vst[i]=1; } if(q[0]>=2) return true; int t=1,x,num; while(t<=q[0]) { x=q[t]; num=0; for(i=1;i<=color;i++) { if(vst[i]) continue; if(!map[x][i]) continue; rd[i]--; if(rd[i]==0) { num++; q[0]++; q[q[0]]=i; vst[i]=1; if(num==2) return true; } } t++; } return false; } int main() { int i,x,y; cin>>T; while(T--) { init(); cin>>n>>m; for(i=1;i<=m;i++) { cin>>x>>y; add(x,y); } if(solve()) puts("Light my fire!"); else puts("I love you my love and our love save us!"); } return 0; }
相关文章推荐
- 2017多校九 05题 hdu 6165 FFF at Valentine 缩点 dp找最长链/拓扑排序
- HDU 6165 - FFF at Valentine DFS暴力 2017 Multi-University Training Contest - Team 9
- hdu-6165 FFF at Valentine
- FFF at Valentine HDU - 6165
- HDU --- 6165 FFF at Valentine 多校第九场 【强联通缩点 + 维护拓扑序】
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- [HDU 6165]FFF at Valentine
- Hdu 6165 FFF at Valentine【Tarjan强连通+暴搜】
- HDU 6165 FFF at Valentine
- hdu 6165 FFF at Valentine(传递闭包)
- HDU 6165 FFF at Valentine(强连通分量+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- HDU-6165 FFF at Valentine - 2017 Multi-University Training Contest - Team 9(强连通分量缩点+拓扑)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- 2017 Multi-University Training Contest - Team 9 1005&&HDU 6165 FFF at Valentine【强联通缩点+拓扑排序】
- HDU 6165 FFF at Valentine
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)
- HDU 6165 FFF at Valentine(tarjan缩点+拓扑排序)
- hdu 6165 FFF at Valentine(强连通分量缩点+拓扑排序)