强连通 缩点
统计入度为0的点即为第一个解
统计出度为0的点,出入度为0的点中的最大值ans即为第二个解
关于第二个问题,给出直观的理解(不是证明)
连通图中,每个点必然有出度和入度,现在用ans条边必然能使图满足每个点都有出度和入度
#include <iostream> #include<stdio.h> #include<string.h> using namespace std; const int inf=1<<30; const int maxn=101; int e[maxn][maxn],dfn[maxn],low[maxn],stack[maxn],visit[maxn],text[maxn],team[maxn]; int time,top,ret; int n; int min(int a,int b) { if(a<b) return(a); return(b); } int tarjan(int u) { dfn[u]=++time,low[u]=dfn[u]; stack[++top]=u; text[u]=1; visit[u]=1; for(int i=1;i<=n;i++) if(e[u][i]) { if(!visit[i]) { tarjan(i); low[u]=min(low[u],low[i]); } else if(text[i]==1) low[u]=min(low[u],dfn[i]); } if(dfn[u]==low[u]) { ret++; while(stack[top]!=u) { int v=stack[top]; team[v]=ret; text[v]=0; top--; for(int i=1;i<=n;i++) { if(e[v][i]) e[u][i]=1; if(e[i][v]) e[i][u]=1; } } team[stack[top]]=ret; text[stack[top]]=0; top--; } } int countin(int t) { int ans=0; for(int i=1;i<=n;i++) if(e[i][t]&&dfn[i]==low[i]&&i!=t) ans++; // printf("%d\n",ans); return(ans); } int countout(int t) { int ans=0; for(int i=1;i<=n;i++) if(e[t][i]&&dfn[i]==low[i]&&i!=t) ans++; return(ans); } int max(int a,int b) { if(a>b) return(a); return(b); } int main() { memset(e,0,sizeof(e)); scanf("%d",&n); for(int i=1;i<=n;i++) { int to; while(scanf("%d",&to),to) { e[i][to]=1; } } memset(text,0,sizeof(text)); memset(visit,0,sizeof(visit)); memset(team,0,sizeof(team)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); time=0; top=0; ret=0; for(int i=1;i<=n;i++) if(!visit[i]) tarjan(i); // for(int i=1;i<=n;i++) // printf("%d ",team[i]); int ans=0; for(int i=1;i<=n;i++) if(dfn[i]==low[i]) if(!countin(i)) ans++; printf("%d\n",ans); int answer=0; for(int i=1;i<=n;i++) if(dfn[i]==low[i]) if(!countout(i)) answer++; if(ret==1) {printf("0\n");return(0);} printf("%d\n",max(ans,answer)); return 0; }