poj-2186 受欢迎的奶牛(tarjan算法应用)
2015-06-13 17:25
337 查看
题意:有n只奶牛,奶牛之间有倾慕的关系,并且倾慕关系是可以传递的。要求找出某几个奶牛,这几个奶牛被所有的奶牛喜欢。
奶牛的关系抽象成为有向图,a喜欢b表示为a点到b点有一条有向边。
思路:
1 通过tarjan算法,求出强连通分量,并且把这些强连通分量缩成缩点。通过这种方法把图转化为有向无环图。
2 在这个有向无环图中,如果存在唯一一个点A,这个点的出度为0,有向无环图中所有的点都有路径到达A。A点所代表的强连通分量中的点就是被所有奶牛喜欢的受欢迎奶牛。
思路详解:tarjan算法的详解以及原理在博客 tarjan算法原理介绍中,点击打开链接,计算缩点是先在tarjan算法出栈scc(强连通分量)的时候记录下每个点的scc编号,然后重新生成一张图。
思路中的步骤2证明如下:
一个有向图,不包含环路。如果有且只有一个点A的出度为0,则任意一个点都有一条路径到A。
运用数学归纳法证明:
1 当只有点A,B,且有B->A这一条路径,结论成立。
2 假设有n个节点和m条有向边,保证没有环路且只有A出度为零,任意一个点都有一条路径到A。
3 加入一个节点Z和任意几条有向边,保证没有环路且只有A出度为零(Z必然有一个出度连向2中的某个节点)。那么在2的基础上,Z可以到达2中的某个节点,而2中每个节点都有一条路径到A,所以Z也有一条路径到A。
综上所述,结论成立。
奶牛的关系抽象成为有向图,a喜欢b表示为a点到b点有一条有向边。
思路:
1 通过tarjan算法,求出强连通分量,并且把这些强连通分量缩成缩点。通过这种方法把图转化为有向无环图。
2 在这个有向无环图中,如果存在唯一一个点A,这个点的出度为0,有向无环图中所有的点都有路径到达A。A点所代表的强连通分量中的点就是被所有奶牛喜欢的受欢迎奶牛。
思路详解:tarjan算法的详解以及原理在博客 tarjan算法原理介绍中,点击打开链接,计算缩点是先在tarjan算法出栈scc(强连通分量)的时候记录下每个点的scc编号,然后重新生成一张图。
思路中的步骤2证明如下:
一个有向图,不包含环路。如果有且只有一个点A的出度为0,则任意一个点都有一条路径到A。
运用数学归纳法证明:
1 当只有点A,B,且有B->A这一条路径,结论成立。
2 假设有n个节点和m条有向边,保证没有环路且只有A出度为零,任意一个点都有一条路径到A。
3 加入一个节点Z和任意几条有向边,保证没有环路且只有A出度为零(Z必然有一个出度连向2中的某个节点)。那么在2的基础上,Z可以到达2中的某个节点,而2中每个节点都有一条路径到A,所以Z也有一条路径到A。
综上所述,结论成立。
#include<iostream> #include<string> #include<iomanip> #include<vector> using namespace std; typedef struct node_arr_elem{ int ID; vector<int> out_adj_node; } node_arr_elem; vector<node_arr_elem> adj_list, scc_adj; int pairs[100][2]; int scc_belonging[10000];//记录每个点属于的强连通分量 int cmp(int index_1, int index_2){ if(pairs[index_1][0]>pairs[index_2][0]){ return 1;//> } else if(pairs[index_1][0]<pairs[index_2][0]){ return -1;//< } else{ if(pairs[index_1][1]>pairs[index_2][1]){ return 1;//> } else if(pairs[index_1][1]<pairs[index_2][1]){ return -1;//< } else{ return 0;//= } } } void sort(int h, int l){ int i,j,temp[2]; if(h>l){ temp[0] = pairs[l][0]; temp[1] = pairs[l][1]; int state = 0; for(i=l,j=h;i<j;){ if(state == 0){ if(cmp(j,l)==-1){ state = 1; pairs[i][0]=pairs[j][0]; pairs[i][1]=pairs[j][1]; } else{ --j; } } if(state == 1){ if(cmp(i,l)==1){ state = 0; pairs[j][0]=pairs[i][0]; pairs[j][1]=pairs[i][1]; } else{ ++i; } } } pairs[i][0] = temp[0]; pairs[i][1] = temp[1]; sort(l,i-1); sort(i+1,h); } } int delete_repeat(int num){ sort(0,num-1); /* for(int i=0; i<num; ++i){ cout<<pairs[i][0]<<"-"<<pairs[i][1]<<endl; } cout<<endl;*/ int j=1,states = 0; for(int i=1;i<num;){ if(pairs[i][0]==pairs[i-1][0]&&pairs[i][1]==pairs[i-1][1]){ ++i; } if(pairs[i][0]!=pairs[i-1][0]||pairs[i][1]!=pairs[i-1][1]){ pairs[j][0] = pairs[i][0]; pairs[j][1] = pairs[i][1]; ++j;++i; } } int new_length = j-1; return new_length; } void create_graph(int n,int m,int choice){//生成图 for(int i=0; i<n; ++i){ node_arr_elem new_n; new_n.ID = i+1; if(choice == 0) adj_list.push_back(new_n); else scc_adj.push_back(new_n); } for(int i=0; i<m; ++i){ if(pairs[i][0]!=pairs[i][1]){ if(choice == 0) adj_list[pairs[i][0]].out_adj_node.push_back(pairs[i][1]); else scc_adj[pairs[i][0]].out_adj_node.push_back(pairs[i][1]); } } } /*tarjan算法*/ vector<int> stack; int low[10000] = {0}; int dfn[10000] = {0}; int time_stamp = 1; int scc_count = 0; int tarjan(int index){ dfn[index] = low[index] = time_stamp++; stack.push_back(index); for(int i=0; i<adj_list[index].out_adj_node.size(); ++i){ int v = adj_list[index].out_adj_node[i]; if(find(stack.begin(),stack.end(),v) != stack.end()){ low[index] = dfn[v]>low[index] ? low[index] : dfn[v]; } else if(dfn[v] == 0){ low[v] = tarjan(v); low[index] = low[v]>low[index] ? low[index] : low[v]; } } if( dfn[index] == low[index] ){//发现强连通分量 //strong_connection s_c; int node; //connection_parts.push_back(s_c); while( stack[stack.size()-1] != index ){ node = stack[stack.size()-1]; //connection_parts[connection_parts.size()-1].nodes.push_back(node); scc_belonging[node] = scc_count;//缩点,记录下这个点所属于的强连通分量 stack.pop_back(); } node = stack[stack.size()-1]; //connection_parts[connection_parts.size()-1].nodes.push_back(node); scc_belonging[node] = scc_count; stack.pop_back(); ++scc_count; } return low[index]; } /*tarjan算法完*/ int main(){ int n, m; cin>>n>>m; for(int i=0; i<m; ++i){ cin>>pairs[i][0]>>pairs[i][1]; --pairs[i][0]; --pairs[i][1]; } create_graph(n,m,0);//生成图 //求强连通分量 并 缩点 for(int finish=0; finish==0;){ int j=-1; for(int i=0; i<n; ++i){ if(dfn[i]==0){ j=i; } } if(j!=-1){ tarjan(j);//求强连通分量 } else{ finish = 1; } } for(int i=0; i<m; ++i){ pairs[i][0] = scc_belonging[pairs[i][0]]; pairs[i][1] = scc_belonging[pairs[i][1]]; } int new_m = delete_repeat(m); create_graph(scc_count,new_m,1); int num=0,scc_no; for(int i=0; i<scc_count; ++i){ if(scc_adj[i].out_adj_node.size()==0){ scc_no = i; ++num; } } if(num==1){ for(int i=0; i<n; ++i){ if(scc_belonging[i] == scc_no){ cout<<i+1<<" "; } } } else{ cout<<"no"; } }
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- C#数据结构与算法揭秘二
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#常见算法面试题小结
- JavaScript 组件之旅(二)编码实现和算法
- JavaScript数据结构和算法之图和图算法