题目大意:给出一张竞赛图中的其中几条单向边,剩下的边随意定向。问最多可以形成多少三元环。
思路:对于任意三个点来说,他们组成了三元环,当且仅当这些点的入度=处度 = 1。如果没有组成三元环,只需要改变这其中任意一条边的方向,使得一个点的入度变成2,一个点的出度变成2。我们只需要算出有多少三个点中有一个点的入度为2的就可以了,并最小化这个东西。
通过公式:ans=C(n,3)-ΣC(degree[x],2)可以发现,我们只需要让每个点的入度尽可能小。由此想到费用流模型(我怎么想不到。。)
类似于x^2这样的函数加入费用流的图中,可以发现,f[2] - f[1] = 3.f[3] - f[2] = 5....我们只需吧1,3,5,7...这些边加进去就行了。费用流肯定会从小往大流,满足f[x] = x^2.
记得之前看到一篇题解,说调了一下午,然后本地测就是不A,一气之下就交上去,然后就A了。
知道为什么么?
这题有SPJ啊!!!
知道我为什么注意到这个事情了么???
因为我***也调了一下午啊!!!
CODE:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define INF 0x3f3f3f3f #define MAX 20010 #define MAXE 5000010 #define S 0 #define T (MAX - 1) using namespace std; #define min(a,b) ((a) < (b) ? (a):(b)) #define max(a,b) ((a) > (b) ? (a):(b)) struct MinCostMaxFlow{ int head[MAX],total; int next[MAXE],last[MAXE],aim[MAXE],flow[MAXE],cost[MAXE]; int f[MAX],from[MAX],p[MAX]; bool v[MAX]; MinCostMaxFlow() { total = 1; memset(head,0,sizeof(head)); } void Add(int x,int y,int f,int c) { next[++total] = head[x]; aim[total] = y; last[total] = x; flow[total] = f; cost[total] = c; head[x] = total; } void Insert(int x,int y,int f,int c) { Add(x,y,f,c); Add(y,x,0,-c); } bool SPFA() { static queue<int> q; while(!q.empty()) q.pop(); memset(f,0x3f,sizeof(f)); memset(v,false,sizeof(v)); f[S] = 0; q.push(S); while(!q.empty()) { int x = q.front(); q.pop(); v[x] = false; for(int i = head[x]; i; i = next[i]) if(flow[i] && f[aim[i]] > f[x] + cost[i]) { f[aim[i]] = f[x] + cost[i]; if(!v[aim[i]]) { v[aim[i]] = true; q.push(aim[i]); } from[aim[i]] = x; p[aim[i]] = i; } } return f[T] != INF; } int EdmondsKarp() { int re = 0; while(SPFA()) { int max_flow = INF; for(int i = T; i != S; i = from[i]) max_flow = min(max_flow,flow[p[i]]); for(int i = T; i != S; i = from[i]) { flow[p[i]] -= max_flow; flow[p[i]^1] += max_flow; } re += max_flow * f[T]; } return re; } }solver; int cnt,total; int src[110][110],t; pair<int,int> fight[MAX]; int in[MAX]; int ans[110][110]; int DFS(int x) { if(x > total) return x - total; for(int i = solver.head[x]; i; i = solver.next[i]) { if(i&1 || solver.flow[i]) continue; return DFS(solver.aim[i]); } return 0; } int main() { cin >> cnt; total = cnt * (cnt - 1) / 2; for(int i = 1; i <= cnt; ++i) for(int x,j = 1; j <= cnt; ++j) { scanf("%d",&x); if(j <= i) continue; fight[++t] = make_pair(i,j); if(x == 2) ++in[i],++in[j],solver.Insert(t,i + total,1,0),solver.Insert(t,j + total,1,0); else if(!x) ++in[j],solver.Insert(t,j + total,1,0); else ++in[i],solver.Insert(t,i + total,1,0); } for(int i = 1; i <= total; ++i) solver.Insert(S,i,1,0); for(int i = 1; i <= cnt; ++i) for(int j = 1; j <= in[i]; ++j) solver.Insert(total + i,T,1,(j << 1) - 1); cout << (cnt * (cnt - 1) * (cnt - 2) / 3 + total - solver.EdmondsKarp()) / 2 << endl; for(int i = 1; i <= total; ++i) { int win = DFS(i); ans[fight[i].first][fight[i].second] = (win == fight[i].first); ans[fight[i].second][fight[i].first] = (win != fight[i].first); } for(int i = 1; i <= cnt; ++i) for(int j = 1; j <= cnt; ++j) printf("%d%c",ans[i][j]," \n"[j == cnt]); return 0; }