您的位置:首页 > 其它

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。

 

综上所述,结论成立。

#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";
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 图论