[bzoj2597][Wc2007]剪刀石頭布【費用流】
阿新 • • 發佈:2019-01-10
【題目連結】
https://www.lydsy.com/JudgeOnline/problem.php?id=2597
【題解】
考慮從總的方案數中減去不合法的方案數,一個不合法的三元環,一定有且只有點的出度為3。所以一個度數為的點會產生個不合法的三元環。
我們的目標是最小化不合法的數量。所以可以用費用流來模擬這個過程。向每個原圖中的點連條邊,流量為,費用為表示每多取一條邊的代價。每條邊往連流量為1費用為0的邊。每個點往他可能以該點為入點的邊連費用為0,流量為1的邊。跑出的最小費用流即為答案。
時間複雜度
【程式碼】
/* - - - - - - - - - - - - - - -
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;
}