1. 程式人生 > >[bzoj2597][Wc2007]剪刀石頭布【費用流】

[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; }