洛谷Oj-信息传递-拓扑排序+DFS/Tarjan强连通分量
来源:互联网 发布:网络视频服务器的安装 编辑:程序博客网 时间:2024/05/17 22:11
问题描述:
有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
80分代码:
struct edge//链式前向星{ int to;//终点 int next;//下一条边};edge e[200010];//边集数组int n,head[200010];int cnt = 1;int ans = inf;//求最小环,所以将答案初始化为无穷大bool loop[200010];//标记void add_edge(int x,int y)//加边{ e[cnt].to = y; e[cnt].next = head[x]; head[x] = cnt; cnt++;}int bfs(int x)//从起点x出发找环{ int sum = 1;//将顶点x计入 int book[200010];//标记,防止进入一个环后陷入死循环 for(int i = 1; i <= n; ++i)//一个小优化,可能会比memset快 book[i] = 0; queue<int> q;//队列 q.push(x);//入队 book[x] = 1;//标记 while(!q.empty()) { int t = q.front();//访问 for(int i = head[t]; i != -1; i = e[i].next)//遍历每一条以t为起点的边 { if(e[i].to == x)//如果回到了x return sum; if(book[e[i].to] == 0)//如果没被标记过 { q.push(e[i].to);//入队 book[e[i].to] = 1;//标记 sum++;//累加 if(sum > ans)//最优化剪枝 return inf; } } q.pop();//出队 } return inf;//返回一个不影响答案的值}void mark(int x)//标记{ queue<int> q;//队列 q.push(x);//入队 loop[x] = true;//标记 while(!q.empty()) { int t = q.front();//访问 for(int i = head[t]; i != -1; i = e[i].next)//遍历每一条以t为起点的边 { if(e[i].to == x)//回到x return; q.push(e[i].to);//入队 loop[e[i].to] = true;//标记 } q.pop();//出队 }}int main(){ cin >> n;//输入 memset(head,-1,sizeof(head));//初始化 for(int i = 1; i <= n; ++i) { int to; scanf("%d",&to); add_edge(i,to);//加边 } for(int i = 1; i <= n; ++i) { if(loop[i] == true)//如果该点在一个环内 continue; int t = bfs(i);//记录 if(t != inf)//环存在 mark(i);//标记环上的每一个点 ans = min(ans,t);//更新答案 } cout << ans << endl; return 0;}
代码②:拓扑排序+DFS
struct edge{ int to; int next;};edge e[200010];int n,head[200010],in_degree[200010],book[200010],t;//数组in_degree记录每个顶点的入度,数组book用来标记该点是否处于环中int cnt = 1;void add_edge(int x,int y)//加边{ e[cnt].to = y; e[cnt].next = head[x]; head[x] = cnt; cnt++;}void topology_sort()//拓扑排序{ queue<int> q;//队列 for(int i = 1; i <= n; ++i)//找出入度为0的顶点,将其入队 if(in_degree[i] == 0) { q.push(i);//入队 book[i] = 1;//标记(删去该点) } while(!q.empty()) { int t = q.front();//访问队首 for(int i = head[t]; i != -1; i = e[i].next)//以顶点t为起点的所有边 { in_degree[e[i].to]--;//其终点入度减1(因为顶点t已经被删去了) if(in_degree[e[i].to] == 0)//如果入度为0 { q.push(e[i].to);//入队 book[e[i].to] = 1;//标记(删去该点) } } q.pop();//出队,别忘了,否则会死循环 }}void dfs(int x){ for(int i = head[x]; i != -1; i = e[i].next)//遍历以顶点x为起点的所有边 if(book[e[i].to] == 0)//如果其终点没被删去(说明其终点处于环中) { t++;//环的大小加1 book[e[i].to] = 1;//标记已经被搜过 dfs(e[i].to);//继续深搜 }}int main(){ cin >> n;//输入 memset(head,-1,sizeof(head));//初始化 for(int i = 1; i <= n; ++i) { int to; scanf("%d",&to); add_edge(i,to);//建边 in_degree[to]++;//终点to的入度加1 } topology_sort();//拓扑排序 int ans = inf;//初始化 for(int i = 1; i <= n; ++i) if(book[i] == 0)//如果不是链 { t = 0;//重置 dfs(i);//深搜,t的值改变 ans = min(ans,t); } cout << ans << endl; return 0;}
代码③:Tarjan求强连通分量
struct edge{ int to; int next;};edge e[200010];int n,head[200010];int dfn[200010],low[200010],book[200010],ts;//数组dfn记录每个顶点的时间戳,数组low记录每个顶点能访问到的顶点中的时间戳的最小值,数组book用来标记,ts为timestamp(时间戳)stack<int> s;//栈,存放强联通分量中的顶点int cnt = 1;int ans = inf;void add_edge(int x,int y)//加边{ e[cnt].to = y; e[cnt].next = head[x]; head[x] = cnt; cnt++;}void Tarjan(int x){ book[x] = 1;//标记 ts++;//时间戳自增 dfn[x] = ts; low[x] = ts; s.push(x);//入栈 for(int i = head[x]; i != -1; i = e[i].next)//访问以顶点x为起点的所有边 { int t = e[i].to;//终点 if(dfn[t] == 0)//如果没被搜索过 { Tarjan(t);//搜索 low[x] = min(low[x],low[t]); } if(book[t] == 1) low[x] = min(low[x],dfn[t]); } if(low[x] == dfn[x]) { int res = 0; while(s.top() != x) { book[s.top()] = 0; s.pop();//出栈 res++;//大小+1 } book[s.top()] = 0; s.pop();//出栈 res++;//大小+1 if(res != 1)//不能是自环 ans = min(ans,res); }}int main(){ cin >> n;//输入 memset(head,-1,sizeof(head));//初始化 for(int i = 1; i <= n; ++i) { int to; scanf("%d",&to); add_edge(i,to);//加边 } for(int i = 1; i <= n; ++i) if(dfn[i] == 0)//如果时间戳为0,即之前没有搜索过该点 Tarjan(i);//搜索 cout << ans << endl; return 0;}
解决方法:
环可能不止一个,所以要求出最小的一个环
要注意由一个点进入环后,如果不标记的话,广搜会死循环
每进行一轮,信息就沿着环流动一次,环有多少条边(顶点),游戏就会进行几轮。
如果一个点在环内,那么下一次就没必要对环内的点进行搜索。比如2->3->4->2,就没必要对3,4进行搜索
分析发现,建图后的情况只能由两种:环,链+环。不存在一条链的情况,因为链的终点是没有出度的。
我们只想求环的大小,如果链过长,就会产生大量时间消耗,每次都可能搜索一条长链后才搜索到环
我们可以联系到拓扑排序的思想,从一个入度为0的点开始,删点,删边,再删点…直到将链删去
之后每次搜索我们就是只对环进行搜索了
Tarjan算法还很迷,硬撑着写一点近乎于废话的注释。以后再学习吧!
阅读全文
0 0
- 洛谷Oj-信息传递-拓扑排序+DFS/Tarjan强连通分量
- NOIP2015信息传递 强连通分量 tarjan
- [BZOJ2330][SCOI2011][拓扑排序][强连通分量][Tarjan]Candy
- 【题解】NOIP 2015 信息传递(tarjan 强连通分量)
- 【BZOJ 1093】【ZJOI 2007】【最大半连通子图】【tarjan强连通分量】【拓扑排序dp】
- 强连通分量tarjan
- 强连通分量 Tarjan
- Tarjan强连通分量
- 强连通分量-Tarjan
- 【图论】强连通分量和拓扑排序
- 洛谷2661-tarjan-强连通分量
- DFS应用:图的拓扑排序以及Kosaraju强连通分量算法
- hdu 5222(Tarjan求强连通分量+dfs)
- NYOJ oj 120 强连通分量之 tarjan
- tarjan求强连通分量
- 强连通分量 Tarjan算法
- POJ1236强连通分量tarjan
- 强连通分量 tarjan算法
- #童游大放映
- webmagic爬虫框架爬取某安卓app视频内容,分析模拟post请求
- App微信支付返回 -1
- Android 反编译Apk
- 管中窥豹IIoT(二):教你选IIoT解决方案-常见功能识别
- 洛谷Oj-信息传递-拓扑排序+DFS/Tarjan强连通分量
- orientdb实战
- 最小的k个数
- 6-通过Java代码build cube
- eclipse中的jar包----tomcat JRE Referenced Libraries WebApp Libraries lib
- Fresco解析 (Controller)
- 关闭sublime3自动更新(要输入license才会奏效)
- Java学习笔记(18)--关键字this详解
- python函数默认参数使用不当解决方法