題意:有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";
}
}