【GDKOI2010】圈地计划

这是一个关于GDKOI2010比赛中图论算法的题目,要求在给定的n*m矩阵中选择0或1以获取最大价值。每个点的价值由A和B矩阵决定,相邻点选择相同会有额外的C矩阵价值。通过最小割(最大流)的方法求解最大价值,建图过程涉及到网络流中的二元关系网络流模型。
摘要由CSDN通过智能技术生成

题目大意

给定一个 n ∗ m n*m nm 的矩阵,每个点可以选择 0 0 0 1 1 1 ,给定数组 A i , j A_{i,j} Ai,j 和数组 B i , j B_{i,j} Bi,j ,表示点 ( i , j ) (i,j) (i,j) 选择 0 0 0 获得 A i , j A_{i,j} Ai,j 的价值,否则获得 B i , j B_{i,j} Bi,j 的价值。
再给定数组 C i , j C_{i,j} Ci,j 表示与 ( i , j ) (i,j) (i,j) 相邻(即点 ( i − 1 , j ) , ( i , j − 1 ) , ( i + 1 , j ) , ( i , j + 1 ) (i-1,j),(i,j-1),(i+1,j),(i,j+1) (i1,j),(i,j1),(i+1,j),(i,j+1)) 且与 ( i , j ) (i,j) (i,j) 选择一样时的附加价值,若有 k k k 个点相邻,则增加贡献为 k ∗ C i , j k*C_{i,j} kCi,j ,求最大的价值。

Input & Output

输入:第一行 n , m n,m n,m ,接下来三个 n ∗ m n*m nm 的矩阵分别表示矩阵 A , B , C A,B,C A,B,C
输出:一个整数,即最大价值。

数据范围

n , m ≤ 100 , 0 ≤ A i , j , B i , j , C i , j ≤ 1000 n,m \le 100,0 \le A_{i,j},B_{i,j},C_{i,j} \le 1000 n,m100,0Ai,j,Bi,j,Ci,j1000

思路

我们可以将题目看作:给定 n ∗ m n*m nm 个点,每个点可以选择 0 0 0 1 1 1 ,二者必须选一个,其中点之间若选相同的就有附加价值,求最大价值。
考虑到每一个点能且只能选一个,根据最小割的定义(最小代价使得图被割为两部分),可以根据总价值 − - 最小割(最大流) = = = 最大价值进行求解。
问题在于建图。矩阵 A A A B B B 建图并不难,源点连向各个点赋为 A A A 的值,表示若不选 A A A 的代价,汇点同理,难点在于点与点之间的连边。
这涉及到了一种网络流模型:二元关系网络流
讲地详细的大佬博客:网络流二元关系
我们只需要在有价值之间的点分别连两条双向边就可以了。
之后就成为网络流版题了。

AC代码

#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
#define INF 21000000
using namespace std;
long long n,m,s,t,cnt,sum,dep[400200],to[400200],nxt[400200],flo[400200];
int head[400200],hed[400200];
long long A[220][220],B[220][220],C[220][220],dian[220][220],nod,f[400400];
void xx(int u,int v,long long w)
{
	cnt++;
	to[cnt]=v;
	flo[cnt]=w;
	nxt[cnt]=head[u];
	head[u]=cnt;
	cnt++;
}
void pd(int a1,int a2,int b1,int b2,long long va)
{
	if(!dian[a1][a2]||!dian[b1][b2])
	return;
	xx(dian[a1][a2],dian[b1][b2],va);
	xx(dian[b1][b2],dian[a1][a2],0);
}
bool bfs()
{
	memset(dep,-1,sizeof(dep));
	memcpy(hed,head,sizeof(head));
	int he=0,ta=1;
	dep[s]=1;
	f[1]=s;
	
	while(he<ta)
	{
		he++;
		int x=f[he];
		for(int i=head[x];i!=-1;i=nxt[i])
		{
			int y=to[i];
//			printf("%d %d %d %d\n",he,x,y,i);
			if(dep[y]==-1&&flo[i])
			{
				dep[y]=dep[x]+1;
				ta++;
				f[ta]=y;
			}
		}
	}
	return dep[t]!=-1;
}
long long dfs(int x,long long flow)
{
//	printf("%d %lld\n",x,flow);
	if(x==t) return flow;
	long long now=flow;
	for(int i=hed[x];i!=-1;i=nxt[i])
	{
		hed[x]=i;
		int y=to[i];
		if(dep[y]==dep[x]+1&&flo[i])
		{
			
		    long long d=dfs(y,min(now,flo[i]));
		    flo[i]-=d,flo[i^1]+=d;
		    now-=d;
		    if(now==0)
		    break;
		}
	}
	return flow-now;
}
long long dinic()
{
	long long ans=0;
	while(bfs())
	ans+=dfs(s,INF);
	return ans; 
}
int main()
{
	memset(head,-1,sizeof(head));
    scanf("%d %d",&n,&m);
    s=0,t=n*m+1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
     nod++,dian[i][j]=nod;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 scanf("%lld",&A[i][j]),sum+=A[i][j],xx(s,dian[i][j],A[i][j]),xx(dian[i][j],s,0);
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 scanf("%lld",&B[i][j]),sum+=B[i][j],xx(dian[i][j],t,B[i][j]),xx(t,dian[i][j],0);
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	 	scanf("%lld",&C[i][j]);
	 	if(i-1>0)
	 	sum=(sum+C[i][j]+C[i-1][j]);
	 	if(j-1>0)
	 	sum=(sum+C[i][j]+C[i][j-1]);
	 }
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	 	pd(i,j,i-1,j,C[i][j]+C[i-1][j]);
	 	pd(i,j,i,j-1,C[i][j]+C[i][j-1]);
	 	pd(i,j,i+1,j,C[i][j]+C[i+1][j]);
	 	pd(i,j,i,j+1,C[i][j]+C[i][j+1]);
	 }
	 printf("%lld\n",sum-dinic());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值