省选专练 WC2007剪刀石头布

本文深入探讨了特殊类别的最大权闭合图问题,通过详细的算法实现和方程求解过程,展示了如何利用SPFA算法和最小成本最大流算法(MCMF)解决此类问题。文章详细介绍了算法的步骤,包括边的添加、节点处理和最短路径的查找,最终实现了问题的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

特殊的类最大权闭合图问题

列出方程求解

注意一个拆平方

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int INF=1e9+7;
const int N=1e6+100;
struct Front_star{
	int u,v,w,c,nxt;
}e[N*4];
int cnt=1;
int first[N]={0};
void addedge(int u,int v,int w,int c){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].c=c;
	e[cnt].nxt=first[u];
	first[u]=cnt;
} 
void add(int u,int v,int w,int c){
	addedge(u,v,w,c);
	addedge(v,u,0,-c);
}
int g[101][101]={0};
int a[101][101]={0};
int n,S=0,T=3e4+100;
int w[N]={0};
int ans=0;
//
int dis[N*4]={0};
int pre[N*4]={0};
queue<int> q;
int inqueue[N*4]={0};
int SPFA(){
	for(int i=0;i<=T;i++)
		dis[i]=INF;
	memset(pre,0,sizeof(pre));
	q.push(S);
	dis[S]=0;
	while(!q.empty()){
		int   x=q.front();
		q.pop();
//		cout<<x<<" "<<'\n';
		inqueue[x]=0;
		for(int   i=first[x];i;i=e[i].nxt){
			int v=e[i].v;
			if(e[i].w&&e[i].c+dis[x]<dis[v]){
				dis[v]=dis[x]+e[i].c;
				pre[v]=i;
				if(!inqueue[v]){
					inqueue[v]=1;
					q.push(v);
				}
			}
		}
	}
	return dis[T]!=INF;
}
int MCMF(){
	int ans=0;
	while(SPFA()){
//		cout<<"-----"<<endl;
		int s=INF;
		for(int i=pre[T];i;i=pre[e[i^1].v])
			s=min(s,e[i].w);
		for(int i=pre[T];i;i=pre[e[i^1].v]){
			e[i].w-=s;
			e[i^1].w+=s;
		}
		ans+=dis[T]*s;
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	S=n*n+1+n;
	T=S+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&g[i][j]);
			if(g[i][j]!=2){
				w[i]+=g[i][j];
			}
		} 
	}
	for(int i=1;i<=n;i++)  {  
        ans+=(w[i]-1)*w[i]/2;  
        int tmp=n*n+i;  
        for(int j=0;j<=n-1;j++) 
			add(tmp,T,1,w[i]+j);  
    }  
    for(int i=1;i<=n-1;i++) 
		for(int j=i+1;j<=n;j++) 
			if(g[i][j]==2){  
        		int tmp=(i-1)*n+j;  
        		add(S,tmp,1,0);  
        		add(tmp,n*n+i,1,0);  
        		add(tmp,n*n+j,1,0);  
    		} 
	/*
	for(int i=1;i<=n;i++){
		ans+=w[i]*(w[i]-1)/2;
		for(int j=0;j<=n-1;j++){
			add(n*n+i,T,1,w[i]+j);//本质是拆平方 
		}
	}
	for(int i=0;i<=n-1;i++){
		for(int j=i+1;j<=n;j++){
			if(a[i][j]==2){
				add(S,(i-1)*n+j,1,0);
				add((i-1)*n+j,n*n+j,1,0);
				add((i-1)*n+j,n*n+i,1,0);
			}
		}
	}*/
	ans+=MCMF();
	for(int i=2;i<=cnt;i++){
//		cout<<e[i].u<<" "<<e[i].v<<'\n';
		if(e[i].u>1&&e[i].u<=n*n&&e[i].v<=(n+1)*n&&!e[i].w){
			int u=e[i].u;
			int x=(u-1)/n+1;
			int y=u-(x-1)*n;
//			cout<<x<<" "<<y<<"_x _y"<<'\n';
			if(e[i].v==n*n+y){
				swap(x,y);	
			}
			a[x][y]=1;
			a[y][x]=0;
		}
	}    
	for(int i=2;i<=cnt;i++) 
		if (e[i].u>=1&&e[i].u<=n*n&&e[i].v<=n*(n+1)&&!e[i].w)  {  
        int tmp=e[i].u;  
        int x=(tmp-1)/n+1,y=tmp-(x-1)*n;  
        if (e[i].v==n*n+y) 
			swap(x,y);  
        a[x][y]=1;a[y][x]=0;  
    }  
	cout<<n*(n-1)*(n-2)/6-ans<<'\n';
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<a[i][j]<<" ";
		}
		cout<<'\n';
	}
	cout<<'\n';
}

转载于:https://www.cnblogs.com/Leo-JAM/p/10079279.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值