Strongly connected HDU - 4635(判断强连通图 缩点)

想法一:

找出强联通块,计算每个连通块内的点数。将点数最少的那个连通块单独拿出来,其余的连通块合并成一个连通分量。 那么假设第一个连通块的 点数是 x  第二个连通块的点数是 y

一个【强】连通图最多(每两个点之间,至少存在一条课互相到达的路径)的边数为n*(n-1)

一个连通图的边数至少为n*(n-1)- x*y + 1

则非连通图最多的边数为n*(n-1)- x*y 即 x*(x-1)+ y*(y-1)+ x*y

因为原图中已经有m条边 所以最多加 x*(x-1)+ y*(y-1)+ x*y - m 条边

这里最少点数的强联通分量要满足一个条件,就是出度或者入度为 0才行,不然是不满足的。

二:

缩点后

这其实就相当于一个完全图至少减去多少条边,使之变成非强连通图

肯定减去连通分量里点最少的那个了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = , INF = 0x7fffffff;
vector<int> G[maxn];
int pre[maxn], low[maxn], cnt[maxn], dfs_clock, scc_cnt, sccno[maxn];
int in[maxn], out[maxn];
stack<int> S;
int n, m; void dfs(int u)
{
pre[u] = low[u] = ++dfs_clock;
S.push(u);
for(int i=; i<G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!sccno[v])
{
low[u] = min(low[u], pre[v]);
}
}
if(low[u] == pre[u])
{
scc_cnt++;
for(;;)
{
int x = S.top(); S.pop();
sccno[x] = scc_cnt; //标记x属于哪一个强连通块
cnt[scc_cnt]++; //统计当前强连通块中元素的个数
if(x == u) break;
}
}
} void init()
{ mem(cnt, );
mem(pre, );
mem(in, );
mem(out, );
mem(low, );
mem(sccno, );
for(int i=; i<=n; i++) G[i].clear();
dfs_clock = ;
scc_cnt = ;
} int main()
{
int T, kase = ;
cin>> T;
while(T--)
{
cin>> n >> m;
init();
for(int i=; i<m; i++)
{
int u, v;
cin>> u >> v;
G[u].push_back(v);
}
for(int i=; i<=n; i++)
if(!pre[i])
dfs(i);
int minx = INF;
for(int i=; i<=n; i++)
for(int j=; j<G[i].size(); j++)
if(sccno[i] != sccno[G[i][j]])
out[sccno[i]]++, in[sccno[G[i][j]]]++;
for(int i=; i<=scc_cnt; i++)
if(in[i] == || out[i] == )
minx = min(minx, cnt[i]);
// cout<< minx <<endl;
printf("Case %d: ",++kase);
if(scc_cnt == ) cout<< "-1" <<endl;
else cout<< n*(n-) - minx*(n-minx) - m <<endl; } return ;
}
上一篇:Shell脚本编程30分钟入门(小结)(转)


下一篇:【转】Chrome快捷键