2597: [Wc2007]剪刀石头布

2597: [Wc2007]剪刀石头布

链接

分析:  

  费用流。

  首先转化一下问题,整张图最优的情况是存在$C_n^3$个,即任意3个都行,然后考虑去掉最少不满足的三元环。

  如果u赢了v,u向v连一条边,如果v有k条入边,那么说明少了$C_k^2$个三元环,所对每场比赛分配度数,求最小费用最大流。

  具体地:S向每场比赛连容量为1,花费为0的边;每场比赛向两个人连容量为1,花费为0的边;每个人因为度数不同,花费不同,所以差分后建边。

  还有一种随机化+迭代的做法。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 20005, INF = 1e9;
struct Edge { int from, to, nxt, cap, cost; } e[100005];
int head[N], pre[N], dis[N], q[100005], deg[N], id[105][105], A[105][105], P[N], tag[N], En = 1, S, T;
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].from = u, e[En].to = v, e[En].cap = f, e[En].cost = w, e[En].nxt = head[u]; head[u] = En;
    ++En; e[En].from = v, e[En].to = u, e[En].cap = 0, e[En].cost = -w, e[En].nxt = head[v]; head[v] = En;
}
bool spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0;
    q[++R] = S, vis[S] = true, dis[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != INF;
}
int mcf() {
    int Cost = 0, Flow = 0;
    while (spfa()) {
        int now = 1e9;
        for (int i = T; i != S; i = e[pre[i]].from) 
            now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].from) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Cost += dis[T] * now;
        Flow += now;
    }
    return Cost;
}
int main() {
    freopen("a.in", "r", stdin);
    int n = read(), tot = n;
    if (n < 3) { printf("0"); return 0; }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= i; ++j) read();
        for (int j = i + 1; j <= n; ++j) {
            int x = read();
            id[i][j] = ++tot;
            if (x == 0) deg[j] ++, tag[tot] = 1;
            else if (x == 1) deg[i] ++, tag[tot] = 2;
            else { add_edge(tot, i, 1, 0); P[tot] = En; add_edge(tot, j, 1, 0); }
        }
    }
    S = 0, T = tot + 1;
    for (int i = n + 1; i <= tot; ++i) add_edge(S, i, 1, 0);
    for (int i = 1; i <= n; ++i) 
        for (int j = deg[i]; j < n - 1; ++j) add_edge(i, T, 1, j);
    int ans = n * (n - 1) * (n - 2) / 6;
    for (int i = 1; i <= n; ++i) 
        ans -= deg[i] * (deg[i] - 1) / 2;
    ans -= mcf();
    printf("%d\n", ans);
    for (int i = 1; i <= n; ++i)
        for (int j = i + 1; j <= n; ++j) {
            if (tag[id[i][j]]) A[i][j] = tag[id[i][j]] - 1;
            else A[i][j] = e[P[id[i][j]]].cap ? 1 : 0;
        }
    for (int i = 1; i <= n; ++i, puts("")) {
        for (int j = 1; j < i; ++j) printf("%d ", A[j][i] ^ 1);
        for (int j = i; j <= n; ++j) printf("%d ", A[i][j]);
    }
    return 0;
}

 

posted @ 2019-01-30 22:04  MJT12044  阅读(138)  评论(0编辑  收藏  举报