tarjan算法之后,把一个图中每一个强联通分量都看作是一个点,就可以把整个图看作是一个DAG,这样的想法很有用啊~
题目描述:
有n个学校连接网络,输入一个协议,协议里面描述了每个学校可以向其他的哪些学校传文件。文件的传递是单向的。输出两个结果,第一个是最少需要多少份文件,第二个是最少在协议上加多少条可以只需要一份文件。
翻译成图论就是这样的,我们把这个图中每个强联通分量看成一个点,那么我们只要给入度为零的强联通分量一份文件那么所有的点都可以得到文件。第二个问题是我们最少添加多少条边可以把整个图变成一个强连通?由于强联通中不存在入度为零或出度为零的点,所以我们把图中的强联通缩点之后,统计出度为零的点和入度为零的点的个数。要添加边条数最少,就把出度为零的点到入度为零的点建一条边,又剩下的再建边,也就是出度为零的点个数和入度为零的点个数中较大的一个。
要注意的是,如果这个图本身是一个强联通,那么第二问的答案就是0,被这个坑了一直wa。。。。
#include <iostream>
#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1005;
struct xx{
int to,next;
}side[maxn*maxn*2];
int node[maxn],low[maxn],dfn[maxn],top,sum,t;
int Ru[maxn],Chu[maxn];
stack<int>s;
void clear(){
top=0;
t=1;
sum=0;
memset(node,-1,sizeof(node));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(Ru,0,sizeof(Ru));
memset(Chu,0,sizeof(Chu));
while(!s.empty()) s.pop();
}
void add(int u,int v){
side[top]=(xx){v,node[u]};
node[u]=top++;
}
void dfs(int u){
dfn[u]=low[u]=t++;
s.push(u);
for(int i=node[u];i!=-1;i=side[i].next){
int v=side[i].to;
if(!dfn[v]) dfs(v);
if(dfn[v]!=-1) low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u]){
int v;
++sum;
do{
v=s.top();s.pop();
low[v]=sum;
dfn[v]=-1;
}while(v!=u);
}
}
int main()
{
clear();
int N;
scanf("%d",&N);
for(int i=1;i<=N;++i){
int v;
while(scanf("%d",&v),v) add(i,v);
}
for(int i=1;i<=N;++i){
if(!dfn[i]) dfs(i);
}
for(int i=1;i<=N;++i){
for(int v=node[i];v!=-1;v=side[v].next){
if(low[i]!=low[side[v].to]){
Ru[low[side[v].to]]++;
Chu[low[i]]++;
}
}
}
int A=0,B=0;
for(int i=1;i<=sum;++i) {
if(!Ru[i]) A++;
if(!Chu[i]) B++;
}
printf("%d\n",A);
if(sum!=1) printf("%d\n",A>B?A:B);
else printf("0\n");
return 0;
}