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 = 300100, 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=0; 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, 0);
    mem(pre, 0);
    mem(in, 0);
    mem(out, 0);
    mem(low, 0);
    mem(sccno, 0);
    for(int i=0; i<=n; i++) G[i].clear();
    dfs_clock = 0;
    scc_cnt = 0;
}



int main()
{
    int T, kase = 0;
    cin>> T;
    while(T--)
    {
        cin>> n >> m;
        init();
        for(int i=0; i<m; i++)
        {
            int u, v;
            cin>> u >> v;
            G[u].push_back(v);
        }
        for(int i=1; i<=n; i++)
            if(!pre[i])
                dfs(i);
        int minx = INF;
        for(int i=1; i<=n; i++)
            for(int j=0; j<G[i].size(); j++)
                if(sccno[i] != sccno[G[i][j]])
                    out[sccno[i]]++, in[sccno[G[i][j]]]++;
        for(int i=1; i<=scc_cnt; i++)
            if(in[i] == 0 || out[i] == 0)
                minx = min(minx, cnt[i]);
       // cout<< minx <<endl;
        printf("Case %d: ",++kase);
        if(scc_cnt == 1) cout<< "-1" <<endl;
        else cout<< n*(n-1) - minx*(n-minx) - m <<endl;


    }

    return 0;
}

 

posted @ 2018-07-13 14:30  WTSRUVF  阅读(278)  评论(0编辑  收藏  举报