• P4249 [WC2007]剪刀石头布 题解


    题目大意

    P4249 [WC2007]剪刀石头布

    给出一个图,图中有些边的方向没有确定,有些方向给定,要求你定下不确定边的方向,使得图中的三元环最多。

    问题求解

    对于三元环的个数,我们直接求解非常困难,所以考虑补集,用总的个数减去不能构成三元环的个数,也就是总的三元环的个数了。

    显然,任意三个点构成三元环的个数是 (C_n^3)

    再来思考不能构成三元环的个数,我们可以发现,如果一个点不能构成三元环,那么这个点的出度为 (2),那么如果一个点的出度为 (du[x]) 的话,不能构成三元环的个数就是 (C_{du[x]}^2=frac{du[x] imes (du[x]-1)}{2})

    所以我们要求的答案就变成了 (C_n^3-sum C_{du[x]}^2)

    因此,要最小化 (sum C_{du[x]}^2) 的值。

    如果一条((u,v))边,假设确定的边我们只能将 (du[v]+1),如果不确实可以将 (du[u]+1)(du[v]+1)(u)(v) 二选一,又注意到边数比较小,所以考虑网络流,将边看成一个"点",从边流向点再流向超级汇点。

    如果一条"边"使得 (du[u]+1) 那么建立一条从这个"边"到 (u) 点的边,流量为 (1),费用为 (0) 如果一条边是没有确定的,那么建立一条到 (u) 点的边和一条到 (v) 的边,流量为 (1),费用为 (0) ,代表二选一,从超级源点到每条"边"也建立一条流量为 (1) ,费用为 (0) 的边,限制流量。

    考虑如何计算答案,观察柿(式)子 (C_{du[x]}^2=frac{du[x] imes (du[x]-1)}{2}) ,发现后面那个柿子很想小学的时候学过的等差数列 (0+1+2+3+...+du[x-1]=frac{du[x] imes (du[x]-1)}{2}) ,那么我们建(du[x]) 条边,从点到超级汇点,流量为 (1),费用分别为 (0,1,2,...,du[x]-1),就可以表示所有的情况了。

    然后就一般的刷费用流就好了。

    代码实现

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=10105,maxe=maxn*8,INF=0x3f3f3f3f;
    int N,mp[105][105],st,ed;
    int lnk[maxn],nxt[maxe],son[maxe],w[maxe],cost[maxe],cnt=1,vis[maxn],dis[maxn],Q[maxn];
    int maxflow,mincost,Ans;
    inline int read(){
    	int ret=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
    	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
    	return ret*f;
    }
    inline int calc(int x,int y){return (x-1)*N+y;}
    inline void add_e(int x,int y,int z,int c){
    	son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;w[cnt]=z;cost[cnt]=c;
    }
    inline void add_edge(int x,int y,int z,int c){
    	add_e(x,y,z,c);add_e(y,x,0,-c);
    }
    int spfa(){
    	int hed=0,til=1;
    	memset(dis,INF,sizeof dis);
    	memset(vis,0,sizeof vis);
    	dis[st]=0;Q[til]=st;
    	while(hed!=til){
    		hed=(hed+1)%maxn;vis[Q[hed]]=0;
    		for(int j=lnk[Q[hed]];j;j=nxt[j])
    			if(w[j]>0&&dis[son[j]]>dis[Q[hed]]+cost[j]){
    				dis[son[j]]=dis[Q[hed]]+cost[j];
    				if(!vis[son[j]]){
    					vis[son[j]]=1;Q[til=(til+1)%maxn]=son[j];
    				}
    			}
    	}
    	return dis[ed]!=INF;
    }
    int min(int x,int y){return x<y?x:y;}
    int DFS(int x,int delta){
    	vis[x]=1;
    	if(x==ed)return delta;
    	int flow=0;
    	for(int j=lnk[x];j;j=nxt[j]){
    		if((!vis[son[j]]||x==ed)&&w[j]>0&&dis[son[j]]==dis[x]+cost[j]){
    			int t=DFS(son[j],min(delta-flow,w[j]));
    			if(t>0){flow+=t;w[j]-=t;w[j^1]+=t;mincost+=t*cost[j];}
    //			if(flow==delta)break;
    		}
    	}
    	return flow;
    }
    inline void Dinic(){
    	while(spfa()){
    		vis[ed]=1;
    		while(vis[ed]){
    			memset(vis,0,sizeof vis);
    			int tmp=DFS(st,INF);
    			maxflow+=tmp;
    		}
    	}
    	Ans=((N-1)*(N-2)*N)/6;
    	Ans-=mincost;
    }
    int main(){
    	N=read();st=0;ed=N*N+N+1;
    	for(int i=1;i<=N;i++)
    	for(int j=1;j<=N;j++){
    		mp[i][j]=read();
    		if(i>=j)continue;
    		if(!(mp[i][j]^2)){
    			add_edge(st,calc(i,j),1,0);
    			add_edge(calc(i,j),N*N+i,1,0);
    			add_edge(calc(i,j),N*N+j,1,0);
    		}
    		else if(!(mp[i][j]^1)) add_edge(st,N*N+j,1,0);
    		else add_edge(st,N*N+i,1,0);
    	}
    	for(int i=1;i<=N;i++)
    	for(int j=0;j<N;j++)
    		add_edge(N*N+i,ed,1,j);
    	Dinic();
    	printf("%d
    ",Ans);
    	for(int i=1;i<=N;i++){
    		for(int j=i+1;j<=N;j++){
    			if(mp[i][j]<2)continue;
    			for(int k=lnk[calc(i,j)];k;k=nxt[k]){
    				if(son[k]&&w[k]==0){
    					mp[i][j]=(son[k]-N*N==j);
    					mp[j][i]=mp[i][j]^1;
    				}
    			}
    		}
    	}
    	for(int i=1;i<=N;i++){
    		for(int j=1;j<=N;j++)printf("%d ",mp[i][j]);
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    吐血分享:QQ群霸屏技术教程2017(活跃篇)
    吐血分享:QQ群霸屏技术(初级篇)
    吐血分享:QQ群霸屏技术教程2017(效益篇)
    吐血分享:QQ群霸屏技术教程(接单篇)
    吐血分享:QQ群霸屏技术教程2017(问题篇)
    吐血分享:QQ群霸屏技术教程(利润篇)
    吐血分享:QQ群霸屏技术教程2017(维护篇)
    用C#实现WEB代理服务器
    Linux基础命令---tload显示系统负载
    Linux基础命令---iostat显示设备状态
  • 原文地址:https://www.cnblogs.com/martian148/p/15191183.html
Copyright © 2020-2023  润新知