Strongly connected——判断强连通、缩点

题目链接

题意:

给一个 n 个节点,m 条边的有向图,求最大可以增加多少条边使得这个有向图仍然不是强连通的。

题解:

定理: 有向图中存在某点的入度或出度为零时,这张有向图不是强连通图 

逆向思维

定义题中所给图为G1,一个有n个点的有向完全图G2

最多增加多少条边使得G1仍不是强连通 ⇒ 最少删除多少条边使得G2不是强连通

缩点

构造一个n个点的有向完全图,需要n*(n-1)条边,而已经给出了m条,故先删去m条,剩下n*(n-1) - m条边

缩点后 假设有a个强连通块,任取其中一个强连通块,(假设取出的这个强连通块里有x个点),剩下的(n-a)个点看成一个强连通块,

扫描二维码关注公众号,回复: 7499704 查看本文章

如果让这两个强连通块之间不联通,肯定是这两个连通块只有一个方向的边,最多就会有x*(n-x)条边  所以最多加n*(n-1)-x*(n-x)-m边。

所以当x最小是式子最大。

即枚举所有出度或入度为0的强连通分量 求出所含点数的最小值(minn)

最后结果即为n * (n - 1) - m - minn * (n - minn)

add:最多增加边的数量

del:最少删除边的数量

m+add=n*(n-1)-del => add=n*(n-1)-m-del  帮助理解

如果刚开始图为强连通的输出 -1

借鉴自 链接

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include<vector>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;//点数
const int maxm = 2e5+5;//边数,因为是无向图,所以这个值要*2

struct Edge
{
    int to,next;
    bool cut;//是否是桥标记
} edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],Stack[maxn],belong[maxn];//belong数组的值是1~scc
int Index,top;
int scc;//边双连通块数/强连通分量的个数
bool Instack[maxn];
int bridge;//桥的数目
int cut[maxn];
int num[maxn];
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut=false;
    head[u] = tot++;
}

void Tarjan(int u,int pre)
{
    int v;
    low[u] = dfn[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    int son=0;
    int flag=0;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        /*
        if(v == pre)
        {
            flag++;
            continue;
        }*/

        if( !dfn[v] )
        {
            son++;
            Tarjan(v,u);
            if( low[u] > low[v] )low[u] = low[v];

            if(low[v] > dfn[u])
            {
                bridge++;
                edge[i].cut = true;
                edge[i^1].cut = true;
            }

            if(u == pre && son > 1)cut[u] = true;
            if(u != pre && low[v] >= dfn[u])cut[u] = true;

        }
        else if( Instack[v] && low[u] > dfn[v] )
            low[u] = dfn[v];
    }

    if(low[u] == dfn[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            belong[v] = scc;
            num[scc]++;

        }
        while( v!=u );
    }

}

void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
int in[maxn],out[maxn];
void solve(int n)
{
    memset(dfn,0,sizeof(dfn));
    memset(Instack,false,sizeof(Instack));
    memset(cut,0,sizeof cut);
    memset(num,0,sizeof num);
    memset(in,0,sizeof in);
    memset(out,0,sizeof out);
    Index = top = scc = 0;
    bridge = 0;
    for(int i = 1; i <= n; i++)
        if(!dfn[i])
            Tarjan(i,i);
}
int main()
{
    int t;
    int n,m;
    int Case=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        int cnt=0;
        for(int i=1; i<=m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addedge(u,v);
        }
        solve(n);
        for(int u=1; u<=n; u++)
        {
            for(int i=head[u]; i!=-1; i=edge[i].next)
            {
                int v=edge[i].to;
                if(belong[u]==belong[v])continue;
                out[belong[u]]++;
                in[belong[v]]++;
            }
        }
        int minn=0x3f3f3f3f;
        for(int i=1; i<=scc; i++)
        {
            if(in[i]==0 || out[i]==0)
            {
                minn=min(minn,num[i]);
            }
        }
        ll ans=n*(n-1)-m;
        ans-=minn*(n-minn);
        if(scc==1)ans=-1;
        printf("Case %d: %lld\n",Case++,ans);
    }
    return 0;
}
/*
5
2 4 3 0
4 5 0
0
0
1 0
*/
View Code

猜你喜欢

转载自www.cnblogs.com/j666/p/11688752.html