http://poj.org/problem?id=1236
//看强联通分量
在有向无环图中,边变为了强连通分量之间的文件传输关系。意味这:只要一个强连通分量有入
边,那么就可以通过这个入边从另外一个分量中接收文件。但是,无环图意味着肯定存在没有入
度(入度为0)的强连通分量,这些强连通分量没有文件来源,所以要作为投放文件的位置。那么,
第一问就只需要计算出缩点后入度为0的强连通分量数目即可。
而第二个问题,把一个有向无环图转换为一个强连通分量。强连通分量的主要特征是:每个点的
入度和出度都不为0,那么计算出入度为0的点的个数SumIn和出度为0的点的个数SumOut,题
目就变为了:在入度为0的点和出出度为0的点之间最少加多少边。很明显的可以看出,答案就是
max(SumIn,SumOut)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=105;
typedef long long ll;
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n;
struct edge{
int v,ne;
}e[N*N];
int h[N],cnt=0;
inline void ins(int u,int v){
cnt++;
e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
}
int dfn[N],low[N],belong[N],dfc,scc;
int st[N],top=0;
void dfs(int u){
dfn[u]=low[u]=++dfc;
st[++top]=u;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(!dfn[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!belong[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
scc++;
while(true){
int x=st[top--];
belong[x]=scc;
if(x==u) break;
}
}
}
int outd[N],ind[N];
void point(){
for(int u=1;u<=n;u++)
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v;
if(belong[u]!=belong[v]) outd[belong[u]]++,ind[belong[v]]++;
}
}
int main(){
n=read();
for(int u=1;u<=n;u++){
int v=read();
while(v!=0){ins(u,v);v=read();}
}
for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
point();
int cnt1=0,cnt2=0;
for(int i=1;i<=scc;i++){
if(ind[i]==0) cnt1++;
if(outd[i]==0) cnt2++;
}
if(scc==1) printf("1\n0");
else printf("%d\n%d",cnt1,max(cnt1,cnt2));
}
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=100+10;
int n,m;
vector<int> G[maxn];
stack<int> S;
int dfs_clock, scc_cnt;
int pre[maxn],low[maxn],sccno[maxn];
int in0[maxn],out0[maxn];
void dfs(int u)
{
pre[u]=low[u]=++dfs_clock;
S.push(u);
for(int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if(!pre[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(!sccno[v])
low[u]=min(low[u],pre[v]);
}
if(low[u]==pre[u])
{
scc_cnt++;
while(true)
{
int x=S.top(); S.pop();
sccno[x]=scc_cnt;
if(x==u) break;
}
}
}
void find_scc(int n)
{
dfs_clock=scc_cnt=0;
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
for(int i=0;i<n;i++)
if(!pre[i]) dfs(i);
}
int main()
{
while(scanf("%d",&n)==1&&n)
{
for(int i=0;i<n;i++) G[i].clear();
for(int u=0;u<n;u++)
{
int v;
while(scanf("%d",&v)==1&&v)
{
v--;
G[u].push_back(v);
}
}//从0开始编号;
find_scc(n);//进行算法,求强连通分量;
for(int i=1;i<=scc_cnt;i++)
in0[i]=out0[i]=true;//目前初始化为true;
for(int u=0;u<n;u++)
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(sccno[u]!=sccno[v])
in0[sccno[v]]=out0[sccno[u]]=false;
//如果不属于一个强连通分量;a->b
//剩余为true的就是强连通分量所在点;
//两个联通分量包含元素个数不同,不为一个强连通分量;
}
int a=0, b=0;
for(int i=1;i<=scc_cnt;i++)
{
if(in0[i]) a++;//入度为0为强连通分量个数;
if(out0[i]) b++;//出度为0
}
if(scc_cnt==1) printf("1\n0\n");//只有一个强连通分量;
else printf("%d\n%d\n",a,max(a,b));//
}
return 0;
}