Popular Cows-Tarjan_SCC
update@2011-09-19
///省略了头文件等等
#define MAX 10010
int N,M;
int head[MAX];
struct edge_node{int u,v,next;}edge[MAX*5];
int component_count;
int belong[MAX];//记录原图中的点属于哪个强连通分量 便于建图
int point_num[MAX];//记录某个缩点中原来的个数
struct edge_node contracted[MAX*5];
int head2[MAX];int edge_count;
void addEdge2(int u,int v){
contracted[edge_count].u = u;
contracted[edge_count].v = v;
contracted[edge_count].next = head2[u];head2[u] = edge_count++;
}
int getNum(){
int zero_outdegree_sum = 0,id;
for(int i = 0;i < component_count;i++){
//遍历所有的缩点 计算出度
//出度为0的点是满足要求的缩点,返回该缩点中点的个数
//注意 如果有解 出度为0的点有且只有一个
int out_degree = 0;
for(int k = head2[i];k != -1;k = contracted[k].next){
out_degree++;
}
if(out_degree==0){zero_outdegree_sum++;id = i;if(zero_outdegree_sum > 1)return -1;}
}
if(zero_outdegree_sum != 1)return -1;
return point_num[id];
}
void contraction(){
//将所有强连通分量缩成点 重新建图
edge_count = 0;//记录新图的边数
memset(head2,-1,sizeof(head2));
for(int i = 1 ;i < N;i++){
for(int j = i+1;j <= N;j++){
if(belong[i] != belong[j]){
//如果它们不在同一个缩点中
int flag = 0;
for(int k = head[i]; k != -1;k = edge[k].next){
if(edge[k].v == j){
addEdge2(belong[i],belong[j]);flag = 1;break;
}
}
if(flag)continue;
for(int k = head[j]; k != -1;k = edge[k].next){
if(edge[k].v == i){
addEdge2(belong[j],belong[i]);break;
}
}
}
}
}
}
int dfs_order[MAX], lowest[MAX];
int my_stack[MAX],visited[MAX],in_stack[MAX];
int mark_num,top;
void tarjan_scc(int u){
dfs_order[u] = lowest[u] = mark_num++;
my_stack[top++] = u;visited[u] = 1;in_stack[u] = 1;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].v;
if(!visited[v]){
tarjan_scc(v);
lowest[u] = mymin(lowest[u],lowest[v]);
}
else if(in_stack[v]){
lowest[u] = mymin(lowest[u],dfs_order[v]);
}
}
if(dfs_order[u] == lowest[u]){
int p,tota=0;
do{
p = my_stack[--top];in_stack[p] = 0;
belong[p] = component_count; tota++;
}while(u != p);
point_num[component_count++] = tota;
}
}
void addEdge(int u,int v){
edge[edge_count].u = u;
edge[edge_count].v = v;
edge[edge_count].next = head[u];head[u] = edge_count++;
}
///main
int main(){
while(scanf("%d%d",&N,&M) != EOF){
edge_count = 0;memset(head,-1,sizeof(head));
for(int i = 1;i <= M;i++){
int a,b;scanf("%d%d",&a,&b);
addEdge(a,b);
}
///init begin
top = 0; memset(visited,0,sizeof(visited));
memset(in_stack,0,sizeof(in_stack));
mark_num = 1;component_count = 0;
memset(point_num,0,sizeof(point_num));
///init end
for(int i = 1;i <= N;i++){
if(visited[i]) continue;
tarjan_scc(i);
}
contraction();//缩点操作
int t = getNum();
if(t==-1)printf("0\n");
else printf("%d\n",getNum());
}
return 0;
}
题目链接:
Popular Cows把tarjan算法弄懂,然后就是求强连通分量,把所有的强连通分量缩成点,找到一个唯一出度为0的点输出缩成该点的以前的点的个数。为了自己理解方便 写的比较繁琐 效率比较低,其实不用重新建图 .....好累啊 不想写了.