调了3个小时,我勒个去。。
下面是转载自http://www.cppblog.com/kuangbin/archive/2011/08/25/154304.html?opt=admin的内容
强连通分量缩点求入度为0的个数和出度为0的分量个数
题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
也就是:
— 给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
— 顶点数<= 100
解题思路:
— 1. 求出所有强连通分量
— 2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
— 3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:
要为每个入度为0的点添加入边,为每个出度为0的点添加出边
假定有 n 个入度为0的点,m个出度为0的点,如何加边?
把所有入度为0的点编号 0,1,2,3,4 ....N -1
每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,
这需要加n条边
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxm=100010;
const int maxn=110;
int ind,cnt,st[maxn],dfn[maxn],low[maxn],size,num[maxn],a[maxn][maxn],n,indegree[maxn],outdegree[maxn],time[maxn],in,out;
bool instack[maxn];
int count(){
if(cnt==1) {in=1; return 0;}
memset(indegree,0,sizeof(indegree));
memset(outdegree,0,sizeof(outdegree));
for(int i=1;i<=n;i++)
for(int j=0;j<num[i];j++)
if(time[a[i][j]]!=time[i]){
indegree[time[a[i][j]]]++;
outdegree[time[i]]++;
}
in=out=0;
for(int i=1;i<=cnt;i++){
if(!indegree[i]) in++;
if(!outdegree[i]) out++;
}
return in>out?in:out;
}
void tarjan(int x){
dfn[x]=low[x]=++ind;
st[++size]=x;
instack[x]=true;
for(int i=0;i<num[x];i++)
if(!dfn[a[x][i]]){
tarjan(a[x][i]);
if(low[x]>low[a[x][i]]) low[x]=low[a[x][i]];
}
else if(instack[a[x][i]]&&dfn[a[x][i]]<low[x]) low[x]=dfn[a[x][i]];
if(low[x]==dfn[x]){
cnt++;
for(int i=st[size--];i!=x;i=st[size--]){ time[i]=cnt; instack[i]=false; } //栈这里调了好久才发现自减要放后面
time[x]=cnt;instack[x]=false;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
#endif
int m;
while(cin>>n){
memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)
while(cin>>m,m)
a[i][num[i]++]=m;
memset(instack,false,sizeof(instack));
memset(dfn,0,sizeof(dfn));
ind=size=cnt=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
int ans=count();
cout<<in<<endl<<ans<<endl;
}
return 0;
}