[WC2007] 剪刀石头布

link

$solution:$

考虑如何求完全图上的三元环个数,考虑对于 $(i,j,k)$ ,若其不是三元环则必有一个的度数为 $2$ ,则总个数为 $$\dbinom{n}{3}-\sum_{i=1}^n \dbinom{d_i}{2}$$ 

若要求式子最小,则 $$min\{\sum_{i=1}^n \dbinom{d_i}{2}\}=min\{\sum_{i=1}^n d_i^2\}$$ 

那么问题便转化为了给每条边定向,使得每个点度数平方的和最小,对于二选一问题考虑网络流求解。维护平方差量,每次增加 $1^2-0^2,2^2-1^2,3^2-2^2,4^2-3^2$ 等,跑最小费用最大流即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=50001;
const int MAXM=301;
queue<int> que;
struct node{
    int u,v,w,cost,nex;
}x[MAXN<<1];
int head[MAXN],cnt,N,M,MM[MAXM][MAXM],S,T,INF=INT_MAX,Me[MAXN][MAXM];
void add(int u,int v,int w,int cost){
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].cost=cost,x[cnt].nex=head[u],head[u]=cnt++;swap(u,v),w=0,cost=-cost;
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].cost=cost,x[cnt].nex=head[u],head[u]=cnt++;return;
}
pair<int,int> pa[MAXN];
int num,dis[MAXN],vis[MAXN],MC;
bool bfs(){
    memset(vis,0,sizeof(vis)),memset(dis,127/3,sizeof(dis));int inf=dis[0];dis[S]=0;
    vis[S]=1;que.push(S);
    while(!que.empty()){
        int xx=que.front();que.pop();vis[xx]=0;
        for(int i=head[xx];i!=-1;i=x[i].nex){
            if(dis[x[i].v]>dis[xx]+x[i].cost&&x[i].w){
                dis[x[i].v]=dis[xx]+x[i].cost;
                if(!vis[x[i].v]) vis[x[i].v]=1,que.push(x[i].v);
            }
        }
    }return dis[T]!=inf;
}
int dfs(int u,int flow){
    if(u==T) return flow;int used=0;vis[u]=1;
    for(int i=head[u];i!=-1;i=x[i].nex){
        if(!vis[x[i].v]&&x[i].w&&dis[x[i].v]==dis[u]+x[i].cost){
            int slow=dfs(x[i].v,min(flow-used,x[i].w));used+=slow;MC+=slow*x[i].cost;
            x[i].w-=slow,x[i^1].w+=slow;
            if(flow==used) break;
        }
    }if(!used) dis[u]=-1;
    return used;
}
int Dinic(){
    int Ans=0;
    while(bfs()){memset(vis,0,sizeof(vis));Ans+=dfs(S,INF);}
    return Ans;
}
int Ans[MAXM][MAXM];
int GG[MAXM][MAXM],d[MAXN];
int calc(int x){if(x<2) return 0;return (x*(x-1))/2;}
int main(){
//    freopen("7.in","r",stdin);
    memset(head,-1,sizeof(head));
    N=read();
    for(int i=1;i<=N;i++) for(int j=1;j<=N;j++){
        int e=read();
        if(e==1) MM[i][j]=1;
        if(e==0) MM[i][j]=2;
    }
    num=N;
    for(int i=1;i<=N;i++){
        for(int j=i+1;j<=N;j++){
            pa[++num].first=i,pa[++num].second=j;GG[i][j]=num;
            if(MM[i][j]==1){add(num,i,1,0);continue;}
            if(MM[i][j]==2){add(num,j,1,0);continue;}
            add(num,i,1,0),add(num,j,1,0);
        }
    }
    for(int i=N+1;i<=num;i++) add(S,i,1,0);T=++num;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++) add(i,T,1,j*j-(j-1)*(j-1));
    }
    int G=Dinic();
    for(int i=0;i<cnt;i++) if(x[i].u>N&&x[i].v<=N) Me[x[i].u][x[i].v]=x[i].w;
    for(int i=1;i<=N;i++){
        for(int j=i+1;j<=N;j++){
            if(MM[i][j]==1){Ans[i][j]=1;continue;}
            if(MM[i][j]==2){Ans[i][j]=0;continue;}
            int las=GG[i][j];
//            printf("%d %d %d\n",las,i,j);
            if(Me[las][i]==1){Ans[i][j]=0;continue;}
            Ans[i][j]=1;continue;
        }
    }
    for(int i=1;i<=N;i++)
        for(int j=i+1;j<=N;j++) Ans[j][i]=Ans[i][j]^1;
    for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) if(Ans[i][j]) d[j]++;
    int res=(N*(N-1)*(N-2))/6;
    for(int i=1;i<=N;i++) res-=calc(d[i]);
    printf("%d\n",res);
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++) printf("%d ",Ans[i][j]);
        printf("\n");
    }return 0;
}/*3
0 1 2
0 0 2
2 2 0
*/
View Code
posted @ 2019-12-17 19:55  siruiyang_sry  阅读(181)  评论(0编辑  收藏  举报