2597: [Wc2007]剪刀石头布
Posted mjtcn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2597: [Wc2007]剪刀石头布相关的知识,希望对你有一定的参考价值。
2597: [Wc2007]剪刀石头布
分析:
费用流。
首先转化一下问题,整张图最优的情况是存在$C_n^3$个,即任意3个都行,然后考虑去掉最少不满足的三元环。
如果u赢了v,u向v连一条边,如果v有k条入边,那么说明少了$C_k^2$个三元环,所对每场比赛分配度数,求最小费用最大流。
具体地:S向每场比赛连容量为1,花费为0的边;每场比赛向两个人连容量为1,花费为0的边;每个人因为度数不同,花费不同,所以差分后建边。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘;return x*f; } const int N = 20005, INF = 1e9; struct Edge { int from, to, nxt, cap, cost; } e[100005]; int head[N], pre[N], dis[N], q[100005], deg[N], id[105][105], A[105][105], P[N], tag[N], En = 1, S, T; bool vis[N]; inline void add_edge(int u,int v,int f,int w) { ++En; e[En].from = u, e[En].to = v, e[En].cap = f, e[En].cost = w, e[En].nxt = head[u]; head[u] = En; ++En; e[En].from = v, e[En].to = u, e[En].cap = 0, e[En].cost = -w, e[En].nxt = head[v]; head[v] = En; } bool spfa() { for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0; int L = 1, R = 0; q[++R] = S, vis[S] = true, dis[S] = 0; while (L <= R) { int u = q[L ++]; vis[u] = false; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) { dis[v] = dis[u] + e[i].cost; pre[v] = i; if (!vis[v]) q[++R] = v, vis[v] = true; } } } return dis[T] != INF; } int mcf() { int Cost = 0, Flow = 0; while (spfa()) { int now = 1e9; for (int i = T; i != S; i = e[pre[i]].from) now = min(now, e[pre[i]].cap); for (int i = T; i != S; i = e[pre[i]].from) e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now; Cost += dis[T] * now; Flow += now; } return Cost; } int main() { freopen("a.in", "r", stdin); int n = read(), tot = n; if (n < 3) { printf("0"); return 0; } for (int i = 1; i <= n; ++i) { for (int j = 1; j <= i; ++j) read(); for (int j = i + 1; j <= n; ++j) { int x = read(); id[i][j] = ++tot; if (x == 0) deg[j] ++, tag[tot] = 1; else if (x == 1) deg[i] ++, tag[tot] = 2; else { add_edge(tot, i, 1, 0); P[tot] = En; add_edge(tot, j, 1, 0); } } } S = 0, T = tot + 1; for (int i = n + 1; i <= tot; ++i) add_edge(S, i, 1, 0); for (int i = 1; i <= n; ++i) for (int j = deg[i]; j < n - 1; ++j) add_edge(i, T, 1, j); int ans = n * (n - 1) * (n - 2) / 6; for (int i = 1; i <= n; ++i) ans -= deg[i] * (deg[i] - 1) / 2; ans -= mcf(); printf("%d ", ans); for (int i = 1; i <= n; ++i) for (int j = i + 1; j <= n; ++j) { if (tag[id[i][j]]) A[i][j] = tag[id[i][j]] - 1; else A[i][j] = e[P[id[i][j]]].cap ? 1 : 0; } for (int i = 1; i <= n; ++i, puts("")) { for (int j = 1; j < i; ++j) printf("%d ", A[j][i] ^ 1); for (int j = i; j <= n; ++j) printf("%d ", A[i][j]); } return 0; }
以上是关于2597: [Wc2007]剪刀石头布的主要内容,如果未能解决你的问题,请参考以下文章