给了 n(2<=n<=105) 个点,从每个点 u 出发连向了一个点 v(共 n 条边)
现在要求添加最少的边使得整个图是一个强连通图
这道题千万不要一般化:先求强连通分量再把图化为 DAG 来做(我们能够很方便的得到需要添加的边的数量,但是加哪些边会变得很麻烦)
注意一个细节:每个点的出度必为 1
有什么特点?
从一个点 u 出发 DFS 遍历所有能够遍历到的点,DFS 结束的时候必定得到一个环!而且,因为每个点的出度为 1,所有遍历到的点只能形成一个环!而且这个环还是在路径的结尾,如果把这个换缩成一个点,那么我们等够得到的是一个“倒着长”的树(只存在从叶子节点到树根的节点,这个环缩成树根了)
如下面的图:
我们把所有的点作为起点 DFS 一遍之后就会得到一系列的这种图,当然,还有一种特殊情况:环!为了便于讲述,我们把它们叫做“分块”
给每个定义一个起点和终点,然后按照下面的做就行了:
当整个图只有一个环的时候,不可能通过加边使得其成为强连通图!
链接相邻的两个分块(分块 A 的终点连向分块 B 的起点)
对于分块中不是起点的入度为 0 的点,建一条反向边
好了,这样加边之后,整个图就以最小的加边数量变成强连通图了
最开始我找出来了强连通分量,然后找出出度为0,入度为0 ,以及出入度都为0的缩点,但是只过了15组数据,后来改来改去只过了10组数据,不知道哪里的问题,希望有大神可以指点一下,我的错误代码:
#include <iostream> #include <stdio.h> #include <string> #include <string.h> #include <vector> const int maxn=200200,maxm=500400; using namespace std; struct Edge { int x,y,next; } e[maxm]; int num[maxn];///每个连通分量含有点个数 int dfn[maxn],low[maxn],v[maxn],s[maxn],b[maxn],h[maxn]; int tot=0,cnt=0,times,t; int n,m; void init() { tot=0; memset(h,0,sizeof(h)); } void add(int x,int y) { e[++tot].x=x; e[tot].y=y; e[tot].next=h[x]; h[x]=tot; } int max(int a,int b) { if(a>b) return a; return b; } int min(int a,int b) { if(a>b) return b; return a; } void Tarjan(int x) { int y,i; times++; t++; dfn[x]=low[x]=times; v[x]=1; s[t]=x; for( i=h[x]; i; i=e[i].next) { y=e[i].y; if(v[y]==0) { Tarjan(y); low[x]=min(low[x],low[y]); } if(v[y]==1) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { cnt++; do { y=s[t--]; b[y]=cnt;///属于哪个强连通分量,cnt也是强连通分量个数1-cnt v[y]=2; num[cnt]++; } while(y!=x); } } void solve(int n) { times=0; t=0; cnt=0; memset(dfn,0,sizeof(dfn)); memset(num,0,sizeof(num)); memset(v,0,sizeof(v)); for(int i=1; i<=n; i++) if(!dfn[i]) Tarjan(i); } int In[maxn],Out[maxn]; int Uin[maxn],Uout[maxn],Uiu[maxn]; vector<int>HD[maxn]; int main() { int t; scanf("%d",&t); init(); for(int i=1; i<=t; i++) { int u; scanf("%d",&u); add(i,u); } solve(t); if(cnt==1) { puts("0"); return 0; } memset(In,0,sizeof(In)); memset(Out,0,sizeof(Out)); memset(Uin,0,sizeof(Uin)); memset(Uout,0,sizeof(Uout)); memset(Uiu,0,sizeof(Uiu)); for(int i=1; i<=t; i++) { for(int j=h[i]; j!=0; j=e[j].next) { int y=e[j].y; HD[b[i]].push_back(i); HD[b[y]].push_back(y); if(b[i]==b[y])continue; Out[b[i]]++; In[b[y]]++; } } int k=0,k2=0,k1=0; for(int i=1; i<=cnt; i++) { if(In[i]==0&&Out[i]==0) { Uiu[k++]=i; } else { if(In[i]==0) Uin[k1++]=i; if(Out[i]==0) Uout[k2++]=i; } } if(k1>k2) { printf("%d\n",k1+k*2); for(int i=0; i<k2; i++) printf("%d %d\n",HD[Uout[i]][0],HD[Uin[i]][0]); for(int i=k2; i<k1; i++) printf("%d %d\n",HD[Uout[0]][0],HD[Uin[i]][0]); for(int i=0; i<k; i++) { printf("%d %d\n",HD[Uin[0]][0],HD[Uiu[i]][0]); printf("%d %d\n",HD[Uiu[i]][0],HD[Uin[0]][0]); } } else { printf("%d\n",k2+k*2); for(int i=0; i<k1; i++) printf("%d %d\n",HD[Uout[i]][0],HD[Uin[i]][0]); for(int i=k1; i<k2; i++) printf("%d %d\n",HD[Uout[i]][0],HD[Uin[0]][0]); if(k2!=0) { for(int i=0; i<k; i++) { printf("%d %d\n",HD[Uout[0]][0],HD[Uiu[i]][0]); printf("%d %d\n",HD[Uiu[i]][0],HD[Uout[0]][0]); } } else { for(int i=0; i<k-1; i++) { printf("%d %d\n",HD[Uiu[i]][0],HD[Uiu[i+1]][0]); printf("%d %d\n",HD[Uiu[i+1]][0],HD[Uiu[i]][0]); } } } return 0; }ac代码,很短,学习的学长的代码:
#include <iostream> #include <vector> #include <stdio.h> #include <string.h> using namespace std; const int maxn=200020; int in[maxn],scl[maxn]; vector <int>e[maxn]; vector<int>In; vector<int>Out; int dfs(int u) { scl[u]=1; int v=e[u][0]; if(!scl[v])return scl[u]=dfs(v); else return scl[u]=u; } int main() { int n,k,t; while(~scanf("%d",&n)) { memset(in,0,sizeof(in)); for(int i=1;i<=n;i++) { int u; scanf("%d",&u); e[i].push_back(u); in[u]++; } k=0; memset(scl,0,sizeof(scl)); for(int i=1;i<=n;i++) { if(!in[i]) { k++; In.push_back(i); Out.push_back(dfs(i)); } } t=k; for(int i=1;i<=n;i++) { if(!scl[i]) { k++; In.push_back(i); Out.push_back(dfs(i)); } } if(k==1&&t==0) k=0; printf("%d\n",k); for(int i=0;i<k;i++) printf("%d %d\n",Out[i],In[(i+1)%k]); } // cout << "Hello world!" << endl; return 0; }