[bzoj2597][Wc2007]剪刀石頭布【費用流】

【題目鏈接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=2597
【題解】
  考慮從總的方案數中減去不合法的方案數,一個不合法的三元環,一定有且只有點的出度爲3。所以一個度數爲k 的點會產生1+2+..k1=k(k1)/2 個不合法的三元環。
  我們的目標是最小化不合法的數量。所以可以用費用流來模擬這個過程。S 向每個原圖中的點連n1 條邊,流量爲1 ,費用爲0..n2 表示每多取一條邊的代價。每條邊往T 連流量爲1費用爲0的邊。每個點往他可能以該點爲入點的邊連費用爲0,流量爲1的邊。跑出的最小費用流即爲答案。
  時間複雜度O(N3)
【代碼】

/* - - - - - - - - - - - - - - -
    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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章