[bzoj2597][Wc2007]剪刀石头布【费用流】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=2597
【题解】
  考虑从总的方案数中减去不合法的方案数,一个不合法的三元环,一定有且只有点的出度为3。所以一个度数为 k 的点会产生 1 + 2 + . . k 1 = k ( k 1 ) / 2 个不合法的三元环。
  我们的目标是最小化不合法的数量。所以可以用费用流来模拟这个过程。 S 向每个原图中的点连 n 1 条边,流量为 1 ,费用为 0.. n 2 表示每多取一条边的代价。每条边往 T 连流量为1费用为0的边。每个点往他可能以该点为入点的边连费用为0,流量为1的边。跑出的最小费用流即为答案。
  时间复杂度 O ( N 3 )
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj2597]
    Points :    min flow
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    NN      110
# define    N       10010
# define    M       1000100
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
int n, mp[NN][NN], p[NN][NN], id[NN][NN], ans;
struct Edge{
    int data, next, l, re, vote;
}e[M];
int head[N], use[N], dis[N], q[N], mn[N], frm[N], place;
void build(int u, int v, int l, int w){
    e[++place].data = v; e[place].next = head[u]; head[u] = place; e[place].vote = w; e[place].l = l; e[place].re = place + 1;
    e[++place].data = u; e[place].next = head[v]; head[v] = place; e[place].vote = -w; e[place].l = 0; e[place].re = place - 1;
}
void spfa(int S, int T){
    memset(use, 0, sizeof(use));
    memset(dis, inf, sizeof(dis));
    int pl = 1, pr = 1; q[1] = S; 
    use[S] = true; mn[S] = inf; dis[S] = 0;
    while (pl <= pr){
        int x = q[(pl++) % N];
        for (int ed = head[x]; ed != 0; ed = e[ed].next)
            if (dis[e[ed].data] > dis[x] + e[ed].vote && e[ed].l != 0){
                dis[e[ed].data] = dis[x] + e[ed].vote;
                mn[e[ed].data] = min(e[ed].l, mn[x]);
                frm[e[ed].data] = ed;
                if (use[e[ed].data] == false){
                    use[e[ed].data] = true;
                    q[(++pr) % N] = e[ed].data;
                }
            }
        use[x] = false;
    }
}
void change(int S, int T){
    int tmp = mn[T];
    while (T != S){
        e[frm[T]].l -= tmp;
        e[e[frm[T]].re].l += tmp;
        T = e[e[frm[T]].re].data;
    }
}
int F(int S, int T){
    int sum = 0, num = 0;
    for (spfa(S, T); dis[T] != inf; spfa(S, T)){
        sum = sum + dis[T] * mn[T];
        num = num + mn[T];
        change(S, T);
    }
    return sum;
}
int main(){
//  freopen("bzoj2597.in", "r", stdin);
//  freopen("bzoj2597.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            mp[i][j] = read();
    int ti = n;
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            p[i][j] = p[j][i] = ++ti;
    int S = ti + 1, T = ti + 2;
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++){
            build(p[i][j], T, 1, 0);
            if (mp[i][j] == 2){
                id[i][j] = place + 1; build(i, p[i][j], 1, 0);
                id[j][i] = place + 1; build(j, p[i][j], 1, 0);
            }
            if (mp[i][j] == 1) { id[i][j] = place + 1; build(i, p[i][j], 1, 0); }
            if (mp[i][j] == 0) { id[j][i] = place + 1; build(j, p[i][j], 1, 0); }
        }
    ans = n * (n - 1) * (n - 2) / 1 / 2 / 3;
    for (int i = 0; i < n - 1; i++){
        for (int j = 1; j <= n; j++)
            build(S, j, 1, i);
        ans -= F(S, T);
    }
    printf("%d\n", ans);
    for (int i = 1; i <= n; i++)
        for (int j = 1, now; j <= n; j++){
            if (id[i][j] == 0 || e[id[i][j]].l == 1)
                now = 0; else now = 1;
            printf("%d%c", now, (j == n) ? ('\n') : (' '));
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/d_vanisher/article/details/80738951
今日推荐