2013 Multi-University Training Contest 4

4635 Strongly connected

题意:给一个n个顶点m条弧的简单有向图(无环无重边),求最多可以添加多少条弧使得添加后的有向图仍为简单有向图且不是一个强连通图,如果给的简单有向图本来就是强连通图,那么输出-1.


分析:

1.用tarjan算法求出强连通分量的个数,如果个数为1,那么输出-1,结束,否则执行2

2.假设将一些强连通分量合并为有n1个顶点简单完全图1,而将剩下的强连通分量合并为n2个顶点的简单完全图2,跨这两个简单完全图的弧的方向只能是单向的,假设m1为完全图1内部的弧的数量,m2为为完全图2内部的弧的数量,m3为跨这两个简单完全图的弧的数量,那么

 ans=n1*(n1-1)-m1+n2*(n2-1)-m2+n1*n2-m3  ----------------------------------------------------1式

 n1+n2=n                                                            ----------------------------------------------------2式

 m1+m2+m3=m                                                 ----------------------------------------------------3式

 n*n=(n1+n2)(n1+n2)=n1*n1+n2*n2+2*n1*n2 -----------------------------------------------------4式

所以

ans=n*n-m-n-n1*n2=n*n-m-n-n1*(n-n1)

ans要最大,所以n1*(n-n1)要最小,同时要跨图1,图2的弧要单向,

所以在跨图1,图2的弧要单向的情况下,n1尽量小。



代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100005
#define INF 1<<30
using namespace std;

typedef struct ArcNode
{
    int adjvex;//该弧所指向的顶点的位置
    struct ArcNode * nextarc;//指向下一条弧的指针
} ArcNode;

typedef struct VNode
{
    int vertex;
    //int In_deg,Out_deg;
    int belong;
    ArcNode * firstarc;
} VNode;

VNode V[MAX];
int DFN[MAX],Stack[MAX],low[MAX];
int top,index,bcnt;
bool instack[MAX];
long long n,m;
int Min;
int cnt;
int k;
int edge[MAX][2];

void init()
{
    int a,b;
    ArcNode * p;
    for(int i=1; i<=n; i++)
    {
        V[i].vertex=i;
        //V[i].In_deg=V[i].Out_deg=0;
        V[i].firstarc =NULL;
    }
    for(int i=0; i<m; i++)
    {
        scanf("%d%d",&a,&b);
       // V[a].Out_deg++;
       // V[b].In_deg++;
       edge[i][0]=a;
       edge[i][1]=b;
        p=(ArcNode*)malloc(sizeof(ArcNode));
        p->adjvex =b;
        p->nextarc =V[a].firstarc ;
        V[a].firstarc =p;
    }
}

void DFS_tarjan(int i)
{
    int j;
    ArcNode * p;
    DFN[i]=low[i]=++index;
    Stack[++top]=i;
    instack[i]=true;
    p=V[i].firstarc ;
    while(p!=NULL)
    {
        j=p->adjvex;
        if(!DFN[j])
        {
            DFS_tarjan(j);
            if(low[j]<low[i])//Low(u)为u的子树能够追溯到的最早的栈中节点的次序号
                low[i]=low[j];
        }
        else if(instack[j]&&DFN[j]<low[i])//Low(u)为u能够追溯到的最早的栈中节点的次序号
            low[i]=DFN[j];
        p=p->nextarc;//
    }
    if(DFN[i]==low[i])
    {
        bcnt++;
        cnt=0;
        int INDEG=0;
        int OUTDEG=0;
        do
        {
            j=Stack[top--];//出栈,j是为该强连通分量中一个顶点
            instack[j]=false;
            //INDEG+=V[j].In_deg;
            //OUTDEG+=V[j].Out_deg;
            V[j].belong=bcnt;
            cnt++;
        }
        while(i!=j);
        for(int kkk=0;kkk<m;kkk++)
        {
            if(V[edge[kkk][0]].belong==bcnt&&V[edge[kkk][1]].belong!=bcnt)
            {
                OUTDEG++;
            }
            if(V[edge[kkk][0]].belong!=bcnt&&V[edge[kkk][1]].belong==bcnt)
                INDEG++;
        }
        if(Min>cnt&&(INDEG==0||OUTDEG==0))
        {
            Min=cnt;
        }
    }
}

void FREE()
{
    ArcNode * p;
    ArcNode * q;
    for(int i=1; i<=n; i++)
    {
        p=V[i].firstarc;
        while(p!=NULL)
        {
            q=p;
            p=p->nextarc;
            free(q);
        }
    }
}

void solve()
{
    int i;
    top=index=bcnt=0;
    memset(DFN,0,sizeof(DFN));
    memset(instack,false,sizeof(instack));
    for(i=1; i<=n; i++)
    {
        if(!DFN[i])
            DFS_tarjan(i);
    }
    //printf("%d\n",bcnt);
    FREE();
    if(bcnt==1)
    {
        printf("Case %d: -1\n",k);
        return;
    }
    long long ans=n*n-n-m-(Min*(n-Min));
    //printf("%d\n",Min);
    printf("Case %d: %lld\n",k,ans);
}

int main()
{
    int t;
    scanf("%d",&t);
    for(k=1; k<=t; k++)
    {
        scanf("%lld%lld",&n,&m);
        Min=INF;
        init();
        solve();
    }
    return 0;
}



你可能感兴趣的:(Tarjan)