P4249 [WC2007] 剪刀石头布
james1BadCreeper · · 题解
如果一个三元组不是三元环,那么有一个点的出度为
考虑最小费用最大流。每个人向汇点连容量为
一个人的贡献可以看成
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[105][105], d[105], id[105][105];
struct Graph {
struct edge {
int u, v, w, c;
edge(int u = 0, int v = 0, int w = 0, int c = 0) :
u(u), v(v), w(w), c(c) {}
};
int n, m, s, t, cur[7005], d[7005];
bool inq[7005];
vector<int> G[7005];
vector<edge> edges;
inline void addedge(int u, int v, int w, int c) {
edges.emplace_back(edge(u, v, w, c));
G[u].emplace_back(edges.size() - 1);
edges.emplace_back(edge(v, u, 0, -c));
G[v].emplace_back(edges.size() - 1);
}
bool SPFA(void) {
memset(cur, 0, sizeof(cur));
memset(d, 0x3f, sizeof(d));
queue<int> q; q.push(s); d[s] = 0; inq[s] = true;
while (!q.empty()) {
int u = q.front(); q.pop(); inq[u] = false;
for (int i = 0; i < G[u].size(); ++i) {
edge &e = edges[G[u][i]];
if (e.w && d[e.v] > d[u] + e.c) {
d[e.v] = d[u] + e.c;
if (!inq[e.v]) q.push(e.v), inq[e.v] = true;
}
}
}
return d[t] < 0x3f3f3f3f;
}
int cost;
bool vis[7005];
int dinic(int x, int res) {
if (x == t) return res;
int flow = 0; vis[x] = true;
for (int i = cur[x]; i < G[x].size() && res; ++i) {
edge &e = edges[G[x][i]]; cur[x] = i;
int c = min(res, e.w);
if (!vis[e.v] && c && d[e.v] == d[x] + e.c) {
int k = dinic(e.v, c);
flow += k; cost += k * e.c; res -= k;
edges[G[x][i]].w -= k; edges[G[x][i] ^ 1].w += k;
}
}
if (!flow) d[x] = 0x3f3f3f3f; vis[x] = false;
return flow;
}
int MCMF(int S, int T) {
s = S; t = T;
int maxflow = 0, flow;
while (SPFA()) while (flow = dinic(s, 1e9)) maxflow += flow;
return cost;
}
} G;
int main(void) {
ios::sync_with_stdio(0);
cin >> n; int S = 0, T = 7001;
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) {
cin >> a[i][j];
if (a[i][j] == 1) ++d[i];
}
for (int i = 1, idx = n; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) if (a[i][j] == 2) {
G.addedge(S, ++idx, 1, 0);
G.addedge(idx, i, 1, 0); id[i][j] = G.edges.size() - 2;
G.addedge(idx, j, 1, 0); id[j][i] = G.edges.size() - 2;
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
ans += d[i] * (d[i] - 1) / 2;
for (int j = d[i] + 1; j <= n; ++j)
G.addedge(i, T, 1, j - 1);
}
ans += G.MCMF(S, T);
cout << n * (n - 1) * (n - 2) / 6 - ans << "\n";
for (int i = 1; i <= n; ++i, cout << "\n")
for (int j = 1; j <= n; ++j, cout << " ") {
if (a[i][j] < 2) cout << a[i][j];
else cout << 1 - G.edges[id[i][j]].w;
}
return 0;
}