题意:
求对一个图补充最少的边使得其成为强连通图.
思路:
缩点是想到了,但是一是对"缩点"认识不够清晰,统计入度出度的时候想着想着又想成了实际的"点",于是就纠结在"不连通的强连通分量没有入度或出度为零的点",然后甚觉错误...
其实啊,"shrink"的时候判断的是一条边两边的id是否相同,这已经是将强连通分量视为一个点了...
一句话, 统计缩点之后的图中入度为零的点的个数, 出度为零的点的个数, 取其大即为答案. 因为从叶子连到根的有向边的条数不会超过这个数, 如果图是不连通的, 将每一个子图先处理成一叶一根(可保持收支平衡),然后首尾相接,仍然是不超过最大叶/根数.
#include <cstdio> #include <cstring> #include <stack> #include <algorithm> using namespace std; const int MAXN = 20005; const int MAXE = 50005; struct pool { int v,pre; }p[MAXE]; int num,head[MAXN]; int dfn[MAXN],low[MAXN],id[MAXN],in[MAXN],out[MAXN]; bool vis[MAXN]; int size,Index,n,m; stack<int> s; void clear() { memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(dfn,0,sizeof(dfn)); memset(head,0,sizeof(head)); memset(p,0,sizeof(p)); memset(vis,false,sizeof(vis)); size = Index = num = 0; while(!s.empty()) s.pop(); } void add(int u, int v) { p[++num].v = v; p[num].pre = head[u]; head[u] = num; } void Tarjan(int u) { low[u] = dfn[u] = ++Index; vis[u] = true; s.push(u); for(int tmp = head[u],k;k = p[tmp].v,tmp;tmp = p[tmp].pre) { if(!dfn[k]) { Tarjan(k); low[u] = min(low[u],low[k]); } else if(vis[k]) low[u] = min(low[u],low[k]); } if(dfn[u]==low[u]) { size++; int k; do { k = s.top();s.pop(); vis[k] = false; id[k] = size; }while(k!=u); } } void shrink() { for(int i=1;i<=n;i++) for(int tmp = head[i],k;k = p[tmp].v,tmp;tmp = p[tmp].pre) if(id[i]!=id[k]) { in[id[k] ]++; out[id[i] ]++; } }//对"缩点"认识不够彻底啊... int main() { int T; scanf("%d",&T); while(T--) { clear();//唉唉唉... scanf("%d %d",&n,&m); for(int i=0,u,v;i<m;i++) { scanf("%d %d",&u,&v); add(u, v); } for(int i=1;i<=n;i++) { if(!dfn[i]) { Tarjan(i); } } shrink(); int in0 = 0, out0 = 0; for(int i=1;i<=size;i++) { if(!in[i]) in0++; if(!out[i]) out0++; } printf("%d\n",(size==1)?0:max(in0,out0)); } }