2 4 0 3 2 1 2 1 3
4 2
题目大意:给一张有向图,求最少加几条边使这个图强连通。
题目分析:先求这张图的强连通分量,如果为1,则输出0,不需要加边已经是强连通的了。否则缩点。遍历原图的所有边,如果2个点在不同的强连通分量里面,建边。构成一张新图。统计新图中点的入度和出度,取入度=0和出度=0的最大值。
因为求强连通缩点后,整张图就变成了一个DAG,要使之强连通,只需要将入度=0和出度=0的点加边即可,要保证加边后没有入度和出度为0的点,所以取两者最大值。
求强连通的话这三种算法任选一种,我用的Gabow算法。
详情请见代码:
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 20001; const int M = 50001; struct edge { int to,next; }g[M]; int head[N]; int stack1[N]; int stack2[N]; int scc[N]; int vis[N]; int in[N]; int out[N]; int n,m; void init() { int i; for(i = 1;i <= n;i ++) { head[i] = -1; scc[i] = vis[i] = in[i] = out[i] = 0; } } void dfs(int cur,int &sig,int &num) { vis[cur] = ++sig; stack1[++stack1[0]] = cur; stack2[++stack2[0]] = cur; for(int i = head[cur];i != -1;i = g[i].next) { if(vis[g[i].to] == 0)//不在栈中,即未访问过 dfs(g[i].to,sig,num); else if(scc[g[i].to] == 0)//在栈中,已访问过,又访问到,所以出现了环,删除环 { while(vis[stack2[stack2[0]]] > vis[g[i].to]) stack2[0] --; } } if(stack2[stack2[0]] == cur) { ++num; stack2[0] --; do { scc[stack1[stack1[0]]] = num; }while(stack1[stack1[0] --] != cur); } } int Gabow() { int i,sig,ret; //memset(vis,0,sizeof(vis)); //memset(scc,0,sizeof(scc)); stack1[0] = stack2[0] = sig = ret = 0; for(i = 1;i <= n;i ++) if(!vis[i]) dfs(i,sig,ret); return ret; } int solve() { int i,j; int num = Gabow(); if(num == 1) return 0; //printf("num:%d\n",num); //memset(in,0,sizeof(in)); //memset(out,0,sizeof(out)); for(i = 1;i <= n;i ++) { for(j = head[i];j != -1;j = g[j].next) { if(scc[i] != scc[g[j].to]) { out[scc[i]] ++; in[scc[g[j].to]] ++; } } } int cnt1,cnt2; cnt1 = cnt2 = 0; for(i = 1;i <= num;i ++) { if(in[i] == 0) cnt1 ++; if(out[i] == 0) cnt2 ++; } return cnt1 > cnt2?cnt1:cnt2; } int nextint() { char c; int ret; while((c = getchar()) > '9' || c < '0') ; ret = c - '0'; while((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + c - '0'; return ret; } void print(int x) { if(!x) return; print(x/10); putchar(x%10 + '0'); } int main() { int i,j,a,b,t; //scanf("%d",&t); t = nextint(); while(t --) { //scanf("%d%d",&n,&m); n = nextint(); m = nextint(); init(); //memset(head,-1,sizeof(head)); for(i = 1;i <= m;i ++) { //scanf("%d%d",&a,&b); a = nextint(); b = nextint(); g[i].to = b; g[i].next = head[a]; head[a] = i; } //printf("%d\n",solve()); int ans = solve(); if(ans == 0) putchar('0'); else print(ans); putchar(10); } return 0; } //46MS 1700K //125MS 1708K