任务A:最少选择多少点能够遍历整张有向图。
任务B:最少添加多少条有向边使整张图变为一个强连通图。
A->先用Tarjan将所有scc缩点,缩点后入度为0的点的个数即为任务A的答案,因为其他点可以由入度为零的点遍历到。
注意:
1、当n=1时,只有一个点,此时任务A的答案为1,任务B的答案为0。
2、当给定图已经是强连通图时,入度为0的点为0,此时任务A和任务B的答案都为0。
B->Tarjan缩点后,我们得到了一张有向无环图(不一定连通)。贪心地想,我们可以将每一个出度为0的点,向入度为0的点连边,这样最少添加的有向边的数量=max{入度为0的点的个数,出度为0的点的个数},而不是两种点的数量之和。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100+10,M=10000; struct node{ int to,nxt; }e[M]; int head[N],tot; void add(int u,int v){ e[++tot]=(node){v,head[u]}; head[u]=tot; } struct ed{ int u,v; }E[M]; int dfn[N],low[N],idx; int s[N],top; bool ins[N]; int col[N],cnt; void tarjan(int u){ dfn[u]=low[u]=++idx; s[++top]=u,ins[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(ins[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ cnt++; while(s[top]!=u){ ins[s[top]]=0; col[s[top]]=cnt; top--; } ins[u]=0; col[u]=cnt; top--; } } int in[N],out[N]; int main(){ int n; int m=0; scanf("%d",&n); for(int i=1;i<=n;i++){ int x; while(1){ scanf("%d",&x); if(!x) break; add(i,x); E[++m]=(ed){i,x}; } } for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } if(cnt==1) { printf("1\n0");return 0; } memset(head,0,sizeof(head)); tot=0; memset(e,0,sizeof(e)); for(int i=1;i<=m;i++){ int u=E[i].u,v=E[i].v; if(col[u]!=col[v]){ add(col[u],col[v]); in[col[v]]++; out[col[u]]++; } } int taskA=0,taskB=0; int indy0=0,outdy0=0; for(int i=1;i<=cnt;i++){ if(!in[i]) taskA++,indy0++;//入度为0的点数 if(!out[i]) outdy0++;//出度为0的点数 } taskB=max(indy0,outdy0); printf("%d\n%d",taskA,taskB); }