poj-2186 受歡迎的奶牛(tarjan算法應用)

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章