正题
[USACO5.3]校园网Network of Schools
第一问:求至少从多少个节点开始,可以遍历整个图。
第二问:求至少加上多少条边,使得无论从哪个节点开始,都可以遍历整张图
首先声明先缩环成点。
第一问就是求入度为0的点即可,因为不是入度为0的话,那么肯定可以从另外一个点传递信息过来。
第二问好像很烦,要求的是加上多少条边可以使图成环。
那么很明显啊,如果成环那么每个点都有入度和出度。
所以我们就想一想答案是不是入度为0或者出度为0的点数的最大值。
好像是对的。。。
怎么证明
首先无视边的方向,它肯定是一棵树。
如果入度比出度大,那么必定能从出度为0的点连边到入度为0的点。剩下的入度为0的点从其他不是入度为0的点随便连即可。
反之则同理。
所以跑一便缩点,用ans1记录入度为0的点数,ans2记录出度为0的点数,输出答案即可。
#include<cstdio> #include<cstdlib> #include<cstring> #include<stack> #include<vector> using namespace std; int n,x; struct edge{ int x,y,next; }s[10010]; struct node{ int dfn,low; }op[110]; int first[110],where[110]; int len=0; stack<int> f; int now=0; bool tf[110]; vector<int> T[110]; int fa[110]; int in[110],out[110]; int tot=0; void ins(int x,int y){ len++; s[len].x=x;s[len].y=y;s[len].next=first[x];first[x]=len; } int findpa(int x){ if(fa[x]!=x) return fa[x]=findpa(fa[x]); return fa[x]; } void Tarjan(int x){ op[x].dfn=op[x].low=++now; tf[x]=true;f.push(x); for(int i=first[x];i!=0;i=s[i].next){ int y=s[i].y; if(op[y].dfn==0){ Tarjan(y); if(op[y].low<op[x].low) op[x].low=op[y].low; } else if(tf[y]==true) if(op[y].dfn<op[x].low) op[x].low=op[y].dfn; } if(op[x].dfn==op[x].low){ tot++; while(1){ int k=f.top(); f.pop(); tf[k]=false; T[tot].push_back(k); where[k]=tot; if(k==x) break; } } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) while(1){ scanf("%d",&x); if(x==0) break; ins(i,x); } for(int i=1;i<=n;i++) if(op[i].dfn==0) Tarjan(i); for(int i=1;i<=tot;i++) fa[i]=i; int ans1=0,ans2=0; for(int i=len;i>=1;i--){ if(where[s[i].x]==where[s[i].y]) continue; in[where[s[i].y]]++; out[where[s[i].x]]++; } for(int i=1;i<=tot;i++){ if(in[i]==0) ans1++; if(out[i]==0) ans2++; } if(tot==1) printf("1\n0");//如果连通分块的数量为1,那么很明显不用连边 else printf("%d\n%d",ans1,max(ans2,ans1)); return 0; }