链接:
题意:
每一头牛都希望在牛群里面备受瞩目,在一个牛群中有N头牛(1<=N<=10000),你被给予M(1<=M<=50000)个关系对,形式如(A,B),这意味着A牛认为B牛比它更受欢迎,由于这种欢迎度是满足传递性的,那么若是A牛认为B牛更受欢迎,B牛认为C牛更受欢迎,那么A牛也会认为C牛更受欢迎。你的任务是计算出被所有牛受欢迎的牛的个数。
输入:
第一行两个整数 N 和 M
第2 到 M + 1 行,两个分开的数 A,B,意味着 A认为 B 更受欢迎。
输出:
被所有牛认为受欢迎的牛的个数
比如输入:
3 3
1 2
2 1
2 3
比如输出:
1
隐藏信息:
3号牛是最后欢迎的
来源于:
USACO 2007 秋季赛
思路:
用tarjan算法求得强连通分量后,将每个SCC缩点,得到的图为 DAG 图,然后寻找出度为 0 的节点(数出该分量中的节点个数),若出度为 0 的节点不止一个,则不存在答案。
#include <iostream> #include <vector> #include <string.h> using namespace std; #define MAX_SIZE 10010 vector< int > Graph[MAX_SIZE]; vector< int > NewGraph[MAX_SIZE]; // 缩点后的图 int nodeBelongToSCC[MAX_SIZE]; // 每个节点属于哪个SCC中 int Stack[MAX_SIZE]; bool nodeIsInStack[MAX_SIZE]; int stack_pointer = 0; int Lows[MAX_SIZE]; int Dfns[MAX_SIZE]; int node_num = 0; // 图中节点得到个数 int edge_num = 0; int find_time = 0; // 每个节点的发现时间 int scc_num = 0; // 记录 scc 的个数 void find_scc( int start_node ){ find_time++; Lows[start_node] = Dfns[start_node] = find_time; stack_pointer++; Stack[stack_pointer] = start_node; nodeIsInStack[start_node] = true; for( int i = 0; i < Graph[start_node].size(); ++i ){ int end_node = Graph[start_node][i]; //若是end_node尚未被访问 if( Dfns[end_node] == 0 ){ find_scc( end_node ); Lows[start_node] = min( Lows[start_node], Lows[end_node] ); } //若end_node在栈中,也就是start_node -> end_node是返祖边 else if( nodeIsInStack[end_node] ){ Lows[start_node] = min( Lows[start_node], Dfns[end_node] ); } } //若是start_node的时间戳与Lows相等在构成SCC if( Dfns[start_node] == Lows[start_node] ){ scc_num++; int pop_node_index = Stack[stack_pointer]; stack_pointer--; nodeIsInStack[pop_node_index] = false; // 设定每个节点的SCC nodeBelongToSCC[pop_node_index] = scc_num; while( start_node != pop_node_index ){ pop_node_index = Stack[stack_pointer]; nodeIsInStack[pop_node_index] = false; stack_pointer--; nodeBelongToSCC[pop_node_index] = scc_num; } } } // 缩点 void shrink(){ for( int start_node = 1; start_node <= node_num; ++start_node ){ int start_scc = nodeBelongToSCC[start_node]; for( int index = 0; index < Graph[start_node].size(); ++index ){ int end_node = Graph[start_node][index]; int end_scc = nodeBelongToSCC[end_node]; // 若是起始 SCC 与 目标 SCC 相同 if( start_scc != end_scc ){ bool exists = false; for( int i = 0; i < NewGraph[start_scc].size(); ++i ){ if( NewGraph[start_scc][i] == end_scc ) exists = true; } // 该分量尚未和目标分量有边相连 if( exists == false ){ NewGraph[start_scc].push_back( end_scc ); } } } } } void init_values(){ memset( nodeBelongToSCC, 0, sizeof( nodeBelongToSCC ) ); memset( Stack, 0, sizeof( Stack ) ); memset( nodeIsInStack, false, sizeof( nodeIsInStack ) ); memset( Lows, 0, sizeof( Lows ) ); memset( Dfns, 0, sizeof( Dfns ) ); } // 统计出度为 0 的分量里面节点的个数 void solve(){ int count_num = 0; int ans_scc = 0; for( int i = 1; i <= scc_num; ++i ){ if( NewGraph[i].size() == 0 ){ ans_scc = i; count_num++; } } int ans = 0; if( count_num == 1 ){ for( int i = 1; i <= node_num; ++i ){ if( nodeBelongToSCC[i] == ans_scc ){ ans++; } } } cout << ans << endl; } int main(){ init_values(); int start_node, end_node; cin >> node_num >> edge_num; for( int i = 1; i <= edge_num; ++i ){ cin >> start_node >> end_node; Graph[start_node].push_back( end_node ); } for( int start_node = 1; start_node <= node_num; ++start_node ){ //该节点尚未被访问到 if( Dfns[start_node] == 0 ){ find_scc( start_node ); } } shrink(); solve(); }
Kosaraju算法解:
#include <vector> #include <fstream> #include <cstring> #include <iostream> using namespace std; const int MAX_SIZE = 10005; vector< int >G[MAX_SIZE]; vector< int >GT[MAX_SIZE]; vector< int >stack; bool isVisit[MAX_SIZE]; int par[MAX_SIZE]; int tree[MAX_SIZE]; int N, M; int countVertex = 0; int parentNum = 0; void initG_GT(){ memset( par, 0, sizeof(par) ); cin>>N>>M; for( int i = 1; i <= M; ++i ){ int start, end; cin>>start>>end; G[start].push_back( end ); GT[end].push_back( start ); } } void DFS_G( int index ){ isVisit[index] = true; for( int i = 0; i < G[index].size(); ++i ){ if( !isVisit[G[index][i]] ) DFS_G( G[index][i] ); } stack.push_back( index ); } void DFS_GT( int index, const int& parent ){ isVisit[index] = true; par[index] = parent; ++tree[parent]; for( int i = 0; i < GT[index].size(); ++i ){ if( !isVisit[GT[index][i]] ) DFS_GT( GT[index][i], parent ); } } void Kosaraju(){ for( int i = 1; i <= N; ++i ){ if( !isVisit[i] ){ DFS_G( i ); } } memset( isVisit, false, sizeof( isVisit ) ); memset( tree, 0, sizeof( tree ) ); for( int i = stack.size() - 1; i >= 0; --i ){ if( !isVisit[stack[i]] ){ parentNum++; DFS_GT( stack[i], parentNum ); } } } void cal(){ int flag = 0; int ans = 0; memset( isVisit, false, sizeof( isVisit ) ); for( int i = 1; i <= N; ++i ){ for( int j = 0; j < G[i].size(); ++j ){ int x = G[i][j]; if( par[i] != par[x] ) isVisit[par[i]] = true; } } for( int i = 1; i <= parentNum; ++i ){ if( !isVisit[i] ){ ++flag; ans = i; } } if( flag == 1 ) cout<<tree[ans]<<endl; else cout<<"0"<<endl; } int main() { initG_GT(); Kosaraju(); cal(); return 0; }
POJ 2186 Popular Cows -- tarjan 缩点,布布扣,bubuko.com
POJ 2186 Popular Cows -- tarjan 缩点
原文地址:http://blog.csdn.net/pandora_madara/article/details/28896525