BZOJ 2597 WC2007 剪刀石头布 费用流

题目大意:给出一张竞赛图中的其中几条单向边,剩下的边随意定向。问最多可以形成多少三元环。


思路:对于任意三个点来说,他们组成了三元环,当且仅当这些点的入度=处度 = 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;
}


你可能感兴趣的:(网络流,费用流,bzoj,神构图,WC2007)