Describe:
求一个有向图加多少条边可以变成一个强连通图
Solution:
Tarjan缩点染色后,判断出度和入度,所有点的出度 = 0 的和 和 入度 = 0 的和的最大值即为所求。
缩点染色
for(int i = 1;i <= n;++i) { if(!dfn[i]) { tarjan(i); } }
void tarjan(int s) { dfn[s] = low[s] = ++tot; stk[stk_siz++] = s; instk[s] = true; for(int i = id[s];~i;i = e[i].pre) { int to = e[i].to; if(!dfn[to]) { tarjan(to); low[s] = min(low[s],low[to]); } else if(instk[to]) low[s] = min(low[s],dfn[to]); } if(dfn[s] == low[s]) { ++colid; while(stk_siz > 0 && stk[stk_siz] != s) { --stk_siz; int tmp = stk[stk_siz]; instk[tmp] = false; col[tmp] = colid; } } }
进行每一个缩点后的出度入度判断
for(int i = 0;i < m;++i) { from = e[i].from; to = e[i].to; //咋忘了缩点了!!这是缩点后的操作 // cout<<from<<" "<<to<<endl; // cout<<col[from]<<" "<<col[to]<<endl; if(col[from] != col[to]) { in[col[to]]++; out[col[from]]++; } } int ret = 0,innum = 0,outnum = 0; for(int i = 1;i <= colid;++i) { if(!in[i])innum++; if(!out[i])outnum++; } ret = max(innum,outnum);
还要注意的就是缩成一个点的时候,也就是本来就是一个强连通分量是不需要添加边的
Coding:
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int maxn = 2e4 + 1e3; const int maxm = 5e4 + 1e3; struct node{ int from,to,pre; node(){} node(int to,int pre):to(to),pre(pre){} }e[maxm]; int colid; int id[maxn],cnt; int col[maxn]; int in[maxn],out[maxn]; int dfn[maxn],low[maxn]; int tot; int stk[maxn],stk_siz; bool instk[maxn]; void add(int from,int to) { e[cnt].to = to; e[cnt].from = from; e[cnt].pre = id[from]; id[from] = cnt++; } void init() { memset(id,-1,sizeof(id)); memset(instk,0,sizeof(instk)); memset(dfn,0,sizeof(dfn)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); cnt = tot = colid = stk_siz = 0; } void tarjan(int s) { dfn[s] = low[s] = ++tot; stk[stk_siz++] = s; instk[s] = true; for(int i = id[s];~i;i = e[i].pre) { int to = e[i].to; if(!dfn[to]) { tarjan(to); low[s] = min(low[s],low[to]); } else if(instk[to]) low[s] = min(low[s],dfn[to]); } if(dfn[s] == low[s]) { ++colid; while(stk_siz > 0 && stk[stk_siz] != s) { --stk_siz; int tmp = stk[stk_siz]; instk[tmp] = false; col[tmp] = colid; } } } int main() { int t,n,m; scanf("%d",&t); while(t--) { init(); scanf("%d%d",&n,&m); int from,to; for(int i = 1;i <= m;++i) { scanf("%d%d",&from,&to); add(from,to); } for(int i = 1;i <= n;++i) { if(!dfn[i]) { tarjan(i); } } //边的存储是从1开始!! for(int i = 0;i < m;++i) { from = e[i].from; to = e[i].to; //咋忘了缩点了!!这是缩点后的操作 // cout<<from<<" "<<to<<endl; // cout<<col[from]<<" "<<col[to]<<endl; if(col[from] != col[to]) { in[col[to]]++; out[col[from]]++; } } int ret = 0,innum = 0,outnum = 0; for(int i = 1;i <= colid;++i) { if(!in[i])innum++; if(!out[i])outnum++; } ret = max(innum,outnum); //特殊判断一下,一个点的时候(一种颜色的时候就是强连通了) if(colid == 1) printf("%d ",0); else printf("%d ",ret); } return 0; }