【强连通 && 最多可以加几条边使得图不为强连通图】HDU

来源:互联网 发布:冒险岛伴侣官方域名 编辑:程序博客网 时间:2024/05/02 01:52

Problem Description

输入T组测试数据,每组测试数据输入n,m分别代表n个点,m条边。
接下来m行,每行u,v代表u->v有一条单向边,问你最多可以加多少条边,图不能是强连通图。

思路:

要使得图不为强连通图,那么至少得有两个缩点,这题核心就在于,将图如何分成两个缩点,使得边最多。一开始,我的思路,将图缩点,然后找到点最少的缩点。令它为一个缩点(由Min个点构成),其他的点为另外一个缩点(由t个点构成也就是n-Min)。那么总共有的边就是t * (t - 1) + Min * (Min - 1) + t * Min,就是两个缩点各自的完全图 + 一个缩点到另一个缩点的所有边。在减m就是结果,提交Wrong了。发现没有考虑最少的缩点的度的情况,如果出度,入度都不为0,那么其他点构成强连通后,整个图就变成强连通图了,所以得找出度或者入度为0的缩点,并且所含的点最少。然后我就思考这样一定成立吗。后面手写了一些情况发现都是成立的,后面改了提交就AC了。

#include<bits/stdc++.h>using namespace std;#define mm 100055#define inf 0x3f3f3f3fstruct node{    int u, to, next;};node Map[mm * 2];int head[mm], vis[mm], dfn[mm], low[mm], Stack[mm], top, sig, N, n;//vis[i]代表i这个点属于vis[i]这个强连通分量里int d[mm], c[mm], r[mm];void add(int u, int v, int &cnt)//前向星{    Map[cnt].u = u;    Map[cnt].to = v;    Map[cnt].next = head[u];    head[u] = cnt++;}void tardfs(int u)//强连通分量 缩点{    low[u] = dfn[u] = sig++;    Stack[top++] = u;    for(int i = head[u]; ~i; i = Map[i].next)    {        int to = Map[i].to;        if(!dfn[to])        {            tardfs(to);            low[u] = min(low[u], low[to]);        }        else if(!vis[to])        {            low[u] = min(low[u], dfn[to]);        }    }    if(low[u] == dfn[u])    {        N++;        do        {            int t = Stack[top - 1];            vis[t] = N;            top--;        }while(Stack[top] != u);    }}void tarjan(){    top = 0, sig = 1, N = 0;    memset(dfn, 0, sizeof(dfn));    memset(vis, 0, sizeof(vis));    for(int i = 1; i <= n; i++)    {        if(!vis[i]) tardfs(i);    }}int main(){    int T, m, u, v, cas = 1, i;    scanf("%d", &T);    while(T--)    {        scanf("%d %d", &n, &m);        int cnt = 0;        memset(head, -1, sizeof(head));        for(i = 0; i < m; i++)        {            scanf("%d %d", &u, &v);            add(u, v, cnt);        }        tarjan();//强连通分量 缩点        printf("Case %d: ", cas++);        if(N == 1) printf("-1\n");//已经为强连通图        else        {            memset(d, 0, sizeof(d));            memset(c, 0, sizeof(c));            memset(r, 0, sizeof(r));            for(i = 0; i < cnt; i++)//缩点后的点的入度,出度是多少            {                u = Map[i].u, v = Map[i].to;                if(vis[u] != vis[v])                {                    c[vis[u]]++; r[vis[v]]++;                }            }            for(i = 1; i <= n; i++)            {                d[vis[i]]++;//vis[i]里面有几个点            }            long long Min = inf;            for(i = 1; i <= N; i++)//找到入度或者出度为0的缩点,里面点最少的点            {                if(Min > d[i]) {                    if(!c[i] || !r[i])                    Min = d[i];                }            }            long long t = n - Min;            printf("%lld\n", t * (t - 1) + Min * (Min - 1) + t * Min - m);//输出        }    }    return 0;}
原创粉丝点击