题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635
先判断图是否强连通。如果不是强连通的,那么缩点。
我们的目的是加最多的边,那么最后的图中,肯定两个集合,这两个集合都是强联通的,
一个集合到一个集合只有单向边。我们先让图是满图,然后通过删边来求的:有n*(n-1)条边,然后删掉已有的边m
,然后还有删掉两个集合的边n1*(n-n1),n1为其中一个集合的顶点个数,因为这里是单向边。
那么答案就是ans=n*(n-1)-m-n1*(n-n1),
我们要使ans最大,那么n1*(n-n1)就要越小
最终添加完边的图,肯定可以分成两个部 X 和 Y ,其中只有X到Y的边没有Y到X的边,那么要使得边数尽可能的多,则X部肯定是一个完全图,Y部也是,同时X部中每个点到Y部的每个点都有一条边,假设X部有x个点,Y部有y个点,有x+y=n,同时边数F=x*y+x*(x-1)+y*(y-1),整理得:F=N*N-N-x*y,(然后去掉已经有了的边m,就是答案),当x+y为定值时,二者越接近,x*y越大,所以要使得边数最多,那么X部和Y部的点数的个数差距就要越大,所以首先对于给定的有向图缩点,对于缩点后的每个点,如果它的出度或者入度为0,那么它才有可能成为X部或者Y部,所以只要求缩点之后的出度或者入度为0的点中,包含节点数最少的那个点,令它为一个部,其它所有点加起来做另一个部,就可以得到最多边数的图了
#include<stdio.h> #include<string.h> #include<vector> #include<algorithm> #define N 100005 #define INF 0xfffffff using namespace std; int head[N], cnt; int top, Is[N], Stack[N], low[N], dfn[N], Time, n, m; int nBlock, Block[N]; int Cnt[N], Out[N], In[N]; struct Edge { int v, next; }e[N]; void Init() { Time = cnt = top = nBlock = 0; memset(Cnt, 0, sizeof(Cnt)); memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(Stack,0 , sizeof(Stack)); memset(Is, 0, sizeof(Is)); memset(Out, 0, sizeof(Out)); memset(In, 0, sizeof(In)); memset(Block, 0, sizeof(Block)); memset(head, -1, sizeof(head)); } void Add(int u, int v) { e[cnt].v = v; e[cnt].next = head[u]; head[u] = cnt++; } void Tajar(int u, int father) { Stack[top++]=u; low[u] = dfn[u] = ++Time; Is[u] = 1; int v; for(int i=head[u]; i!=-1; i=e[i].next) { v = e[i].v; if(!dfn[v]) { Tajar(v, u); low[u] = min(low[u], low[v]); } else if(Is[v]) low[u] = min(low[u], dfn[v]); } if(low[u]==dfn[u]) { ++nBlock; do { v=Stack[--top]; Is[v] = 0; Block[v] = nBlock; Cnt[nBlock]++; }while(u!=v); } } int main() { int T, t=1, x, y; scanf("%d", &T); while(T--) { Init(); scanf("%d%d", &n, &m); for(int i=1; i<=m; i++) { scanf("%d%d", &x, &y); Add(x, y); } for(int i=1; i<=n; i++) { if(!low[i]) Tajar(i, -1); } for(int i=1; i<=n; i++) { for(int j=head[i]; j!=-1; j=e[j].next) { int u = Block[i]; int v = Block[e[j].v]; if(u != v) { Out[v]++; In[u]++; } } } y = INF; for(int i=1; i<=nBlock; i++) { if(!In[i] || !Out[i]) y=min(y, Cnt[i]); } x = n - y; long long ans=(long long)n*(n-1)-x*y-m; if(nBlock==1) printf("Case %d: -1 ", t++); else printf("Case %d: %lld ", t++, ans); } return 0; }
一年之后又来写
#include<iostream> #include<stdio.h> #include<algorithm> #include<math.h> #include<string.h> #include<string> #include<stack> #include<vector> #include<map> using namespace std; #define N 100305 #define INF 0x3f3f3f3f #define met(a, b) memset(a, b, sizeof(a)) typedef long long LL; vector<vector<int> >G; stack<int>sta; int low[N], dfn[N], block[N], Block, vis[N]; int In_degree[N], Out_degree[N], Time, n, cnt[N]; void Init() { met(low, 0); met(dfn, 0); met(block, 0); met(vis, 0); met(In_degree, 0); met(Out_degree, 0); met(cnt, 0); G.clear(); G.resize(n+2); while(sta.size())sta.pop(); Time = Block = 0; } void Tarjan(int u) { low[u] = dfn[u] = ++Time; sta.push(u); vis[u] = 1; int len = G[u].size(), v; for(int i=0; i<len;i++) { v = G[u][i]; if(!dfn[v]) { Tarjan(v); low[u] = min(low[u], low[v]); } else if(vis[v]) { low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]) { Block++; do{ v = sta.top(); sta.pop(); block[v] = Block; cnt[Block]++; vis[v] = 0; }while(u!=v); } } int main() { int T, m, t = 1, u, v; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &m); Init(); for(int i=1; i<=m; i++) { scanf("%d %d", &u, &v); G[u].push_back(v); } for(int i=1; i<=n; i++) { if(!dfn[i]) Tarjan(i); } if(Block == 1) { printf("Case %d: -1 ", t++); continue; } for(int i=1; i<=n; i++) { int len = G[i].size(); for(int j=0; j<len; j++) { u = block[i]; v = block[G[i][j]]; if(u != v) { In_degree[v]++; Out_degree[u]++; } } } int ans = 0 , sum = n*(n-1) - m; for(int i=1; i<=Block; i++) { if(!In_degree[i] || !Out_degree[i]) ans = max(ans, sum - cnt[i]*(n-cnt[i])); } printf("Case %d: %d ", t++, ans); } return 0; }