链接:点击打开链接
题意:问一个有向图最多添加多少条边使得这个图仍不是强连通图,如果这个图已经是强连通图则直接输出-1
代码:
#include <vector> #include <cstdio> #include <cstring> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; const int INF=0x3f3f3f3f; int V,E; vector<int> G[100005]; vector<int> rG[100005]; vector<int> vs; bool used[100005]; int in[100005],out[100005]; int x[100005],y[100005],cmp[1000005],cnt[1000005]; void addedge(int from,int to){ G[from].push_back(to); rG[to].push_back(from); } void dfs(int v){ int i; used[v]=1; for(i=0;i<G[v].size();i++) if(!used[G[v][i]]) dfs(G[v][i]); vs.push_back(v); } void rdfs(int v,int k){ int i; used[v]=1; cmp[v]=k; for(i=0;i<rG[v].size();i++) if(!used[rG[v][i]]) rdfs(rG[v][i],k); } int scc(){ int i,k; memset(used,0,sizeof(used)); vs.clear(); for(i=0;i<V;i++) if(!used[i]) dfs(i); memset(used,0,sizeof(used)); k=0; for(i=vs.size()-1;i>=0;i--) if(!used[vs[i]]) rdfs(vs[i],k++); return k; } //强连通模板 int main(){ //因为要使得最后图不是强连通,并且 int t,i,j,ans,cas,tmp; //添加的边最多,因此最后图的强连通 scanf("%d",&t); //分量应为2,并且两部分都是完全图, for(cas=1;cas<=t;cas++){ //并且两部分之间可连接单向边 scanf("%d%d",&V,&E); for(i=0;i<V;i++){ //因为两部分都是完全图,因此问题变为 G[i].clear(); //V*(V-1)条边减去E后最少再减去多少条 rG[i].clear(); //也就是两个强连通分量可以连的单向边 vs.clear(); //的数量,有因为x+y和一定时,两个数 } //差距越大x*y越小,因此选择最小的x或y for(i=0;i<E;i++){ //作为其中一个部分 scanf("%d%d",&x[i],&y[i]); x[i]--,y[i]--; addedge(x[i],y[i]); } ans=scc(); if(ans==1){ //如果是强连通分量直接输出-1 printf("Case %d: -1\n",cas); continue; } memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(cnt,0,sizeof(cnt)); for(i=0;i<V;i++) //数出每个强连通分量中的点的个数 cnt[cmp[i]]++; for(i=0;i<E;i++){ if(cmp[x[i]]!=cmp[y[i]]){ //记录每个强连通分量的入度和出度 out[cmp[x[i]]]++; in[cmp[y[i]]]++; } } tmp=INF; for(i=0;i<V;i++) if(in[cmp[i]]==0||out[cmp[i]]==0) tmp=min(tmp,cnt[cmp[i]]); printf("Case %d: %d\n",cas,V*(V-1)-E-tmp*(V-tmp)); } return 0; }