poj 1236一个简单栗子?
给定一个有向图,如果一个点有了一个软件,它能到达的所有点都可以使用这个软件。ask1:问你至少需要给多少个学校配备软件,使所有的学校都可以使用到软件;ask2:问你最少需要建立多少条联系,使得你在任意一个学校安置一个软件之后,所有的学校就都可以使用软件了。
简单分析一下,一个环内所有的点可以互相到达,如果某一个学校得到了软件,其他的都可以使用。所以我们需要tarjan跑强连通图进行缩点,缩点完之后建一张新的图,这个图是严格拓扑的。对于ask1,其实就是求一下入度为0的点的个数;对于ask2,其实可以转化为你在新图上最少添加多少条边,让它成为一个环?那么其实就是入度为0的个数和出度为0的点的个数之间取一个max,特别的,如果缩点之后整张图是一个点,那么ask2=0;
#include<bits/stdc++.h> using namespace std; const int N=200; const int M=4e4+10; int n,m,tot,tc,top,cnt,num,ver[M],lin[N],Next[M],vc[M],nc[M],lc[N],c[N],dfn[N],low[N],s[N],ins[N],rd[N],cd[N]; void add(int x,int y){ ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot; } void addc(int x,int y){ vc[++tc]=y;nc[tc]=lc[x];lc[x]=tc;rd[y]++;cd[x]++; } void tarjan(int x){ dfn[x]=low[x]=++num; s[++top]=x,ins[x]=1; for(int i=lin[x];i;i=Next[i]){ int y=ver[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); }else if(ins[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ cnt++;int y; do{ y=s[top--];c[y]=cnt;ins[y]=0; }while(x!=y); } } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ int x; while(scanf("%d",&x),x) add(i,x); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int x=1;x<=n;++x){ for(int i=lin[x];i;i=Next[i]){ int y=ver[i]; if(c[x]==c[y]) continue; addc(x,y); } } int sum1=0,sum2=0; for(int i=1;i<=cnt;++i){ if(rd[i]==0) sum1++; if(cd[i]==0) sum2++; } printf("%d\n%d\n",sum1,max(sum1,sum2)); return 0; }