链接:
题意:
每一头牛都希望在牛群里面备受瞩目,在一个牛群中有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