2597: [Wc2007]剪刀石头布
Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 1146 Solved: 538
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
0 1 2
0 0 2
2 2 0
Sample Output
0 1 0
0 0 1
1 0 0
题解:直接求合法三元组(A,B,C)不好求,想到可以用补集:如果(A,B,C)不满足石头剪刀布,那么一定有一个赢了2场,一个赢了1场,一个赢了0场。
那么对于每个人i,设他赢了w[i]场,他能构成的不合法的三元组就有C(w[i],2)个,C为组合数。那么最多能组成的合法三元组个数为:ans=C(n,3)-ΣC(w[i],2),对于网络流的所有最大流都是他的合法解。
要使ans最大,就要是的ΣC(w[i],2)最小,可以想到用最小费用流来做。
首先建n个点P[i]向汇点T连n-1条边,表示第i个人进行了n-1场比赛,花费为0~n-2。
再建n*n个点与源点连接,k[i,j]表示i和j进行比赛,如果mp[i][j]=2,即比赛还没开始,那么分别向P[i]和P[j]连一条容量为1费用为0的边;如果mp[i][j]=1,即i已经赢了j,则向P[i]连一条容量为1费用为0的边;如果mp[i][j]=0,即j已经赢了i,则向P[j]连一条容量为1费用为0的边。
跑一遍费用流,得到的最小费用即是ΣC(w[i],2)
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int MX = 15555;
const int MXE = MX * 100;
struct MinCost_MaxFlow {
struct Edge {
int v, w, nxt;
int cost;
} edge[MXE];
int head[MX], tot, level[MX], pre[MX], d[MX];
bool vis[MX];
void init() {
memset(head, -1, sizeof(head));
tot = 0;
}
void add(int u, int v, int w, int cost) {
edge[tot].v = v;
edge[tot].w = w;
edge[tot].cost = cost;
edge[tot].nxt = head[u];
head[u] = tot++;
edge[tot].v = u;
edge[tot].w = 0;
edge[tot].cost = -cost;
edge[tot].nxt = head[v];
head[v] = tot++;
}
bool spfa(int s, int t) {
memset(vis, 0, sizeof(vis));
memset(d, 0x3f, sizeof(d));
memset(pre, -1, sizeof(pre));
queue<int>q;
q.push(s);
d[s] = 0;
vis[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int w = edge[i].w, v = edge[i].v, cost = edge[i].cost;
if (w > 0 && d[v] > d[u] + cost) {
d[v] = d[u] + cost;
pre[v] = i;
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
return pre[t] != -1;
}
int solve(int s, int t,int &cost) {
int flow = 0;
cost = 0;
while (spfa(s, t)) {
int minFlow = inf;
for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v])
minFlow = min(minFlow, edge[i].w);
for (int i = pre[t]; ~i; i = pre[edge[i ^ 1].v]) {
cost += minFlow * edge[i].cost;
edge[i].w -= minFlow;
edge[i ^ 1].w += minFlow;
}
flow += minFlow;
}
return flow;
}
} F;
int n,mp[105][105],tot,ans;
int mark[105][105];
void build(){
memset(mark,0,sizeof(mark));
int sz=0;
tot=n*(n-1)/2;
F.init();
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
F.add(0,++sz,1,0);
if(mp[i][j]==1||mp[i][j]==2) {
mark[i][j]=F.tot;
F.add(sz,tot+i,1,0);
}
if(mp[i][j]==0||mp[i][j]==2) {
mark[j][i]=F.tot;
F.add(sz,tot+j,1,0);
}
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<n-1;j++)
F.add(tot+i,tot+n+1,1,j);
}
int main(){
// freopen("in.txt","r",stdin);
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
build();
F.solve(0,tot+n+1,ans);
printf("%d\n",n*(n-1)*(n-2)/6-ans);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) printf("0%c",j==n?'\n':' ');
else printf("%d%c",mark[i][j]==0||F.edge[mark[i][j]].w?0:1,j==n?'\n':' ');
}
}
}
return 0;
}