BZOJ2597: [Wc2007]剪刀石头布

BZOJ2597: [Wc2007]剪刀石头布

https://lydsy.com/JudgeOnline/problem.php?id=2597

分析:

  • 好题。
  • 先是补集转化,求最少的非剪刀石头布情况。
  • 我们枚举那个赢了两次的人,可知总数就是\(\sum\limits _ {i=1}^{n}\binom{w_i}{2}\)其中\(w_i\)为获胜场数。
  • 那么对于没打过比赛的两个人,新建点\(tot\)\(S\rightarrow tot\) \(tot\)指向那两个点。
  • 然后每个人向\(T\)\(n\)边,费用为\(\binom{i}{2}-\binom{i-1}{2}\)

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <set>
using namespace std;
#define N 11050
#define M 500050
#define inf 0x3f3f3f3f
const int S=N-1,T=N-2;
int head[N],to[M],nxt[M],flow[M],val[M],n,cnt=1;
int dis[N],Q[N],path[N],vis[N];
inline void add(int u,int v,int f,int c) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; flow[cnt]=f; val[cnt]=c;
    to[++cnt]=u; nxt[cnt]=head[v]; head[v]=cnt; flow[cnt]=0; val[cnt]=-c;
}
bool spfa() {
    memset(path,0,sizeof(path));
    memset(dis,0x3f,sizeof(dis));
    int l=0,r=0;
    Q[r++]=S; dis[S]=0;
    while(l!=r) {
        int x=Q[l++]; if(l==S) l=0;
        int i; vis[x]=0;
        for(i=head[x];i;i=nxt[i]) if(dis[to[i]]>dis[x]+val[i]&&flow[i]) {
            dis[to[i]]=dis[x]+val[i]; path[to[i]]=i^1;
            if(!vis[to[i]]) {
                vis[to[i]]=1; Q[r++]=to[i]; if(r==S) r=0;
            }
        }
    }
    return path[T]!=0;
}
int ek() {
    int minc=0;
    while(spfa()) {
        int nf=inf;
        int i;
        for(i=T;i!=S;i=to[path[i]]) {
            nf=min(nf,flow[path[i]^1]);
        }
        for(i=T;i!=S;i=to[path[i]]) {
            flow[path[i]^1]-=nf;
            flow[path[i]]+=nf;
            minc+=nf*val[path[i]^1];
        }
    }
    return minc;
}
int A[105][105],pp[105][105];
int main() {
    scanf("%d",&n);
    int i,j;
    for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&A[i][j]);
    int tot=n;
    for(i=1;i<=n;i++) {
        for(j=i+1;j<=n;j++) if(A[i][j]==2) {
            tot++;
            add(tot,i,1,0);
            pp[i][j]=cnt-1;
            add(tot,j,1,0);
            pp[j][i]=cnt-1;
            add(S,tot,1,0);
        }
    }
    for(i=1;i<=n;i++) {
        int c=0;
        for(j=1;j<=n;j++) {
            if(A[i][j]==1) c++;
        }
        if(c) add(S,i,c,0);
    }
    for(i=1;i<=n;i++) {
        for(j=1;j<=n;j++) {
            add(i,T,1,j*(j-1)/2-(j-1)*(j-2)/2);
        }
    }
    int ans=ek();
    ans=n*(n-1)*(n-2)/6-ans;
    printf("%d\n",ans);
    for(i=1;i<=n;i++) {
        for(j=1;j<=n;j++) {
            if(A[i][j]!=2) {
                printf("%d ",A[i][j]);
            }else {
                if(i==j) printf("0 ");
                else {
                    if(!flow[pp[i][j]]) printf("1 ");
                    else printf("0 ");
                }
            }
        }
        puts("");
    }
}

猜你喜欢

转载自www.cnblogs.com/suika/p/10205854.html
今日推荐