题意:给一个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#include #include #define MAX 100005#define INF 1<<30using 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 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] 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 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;}