poj-2186 受欢迎的奶牛(tarjan算法应用)

来源:互联网 发布:阿里云市场镜像安全吗 编辑:程序博客网 时间:2024/05/29 11:32

题意:有n只奶牛,奶牛之间有倾慕的关系,并且倾慕关系是可以传递的。要求找出某几个奶牛,这几个奶牛被所有的奶牛喜欢。


奶牛的关系抽象成为有向图,a喜欢b表示为a点到b点有一条有向边。


思路:

1 通过tarjan算法,求出强连通分量,并且把这些强连通分量缩成缩点。通过这种方法把图转化为有向无环图。

2 在这个有向无环图中,如果存在唯一一个点A,这个点的出度为0,有向无环图中所有的点都有路径到达A。A点所代表的强连通分量中的点就是被所有奶牛喜欢的受欢迎奶牛。


思路详解:tarjan算法的详解以及原理在博客  tarjan算法原理介绍中,点击打开链接,计算缩点是先在tarjan算法出栈scc(强连通分量)的时候记录下每个点的scc编号,然后重新生成一张图。

思路中的步骤2证明如下:

一个有向图,不包含环路。如果有且只有一个点A的出度为0,则任意一个点都有一条路径到A

运用数学归纳法证明:

当只有点AB,且有B->A这一条路径,结论成立。

假设有n个节点和m条有向边,保证没有环路且只有A出度为零,任意一个点都有一条路径到A

加入一个节点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);elsescc_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]);elsescc_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";}}


0 0
原创粉丝点击