SNOI省选模拟赛Round3 T2 游戏game 最小割

本文介绍了一个游戏场景下的矩阵收益最大化问题,通过构建最小割模型解决包含额外任务的矩阵操作问题,实现收益的最大化。

B 游戏(game.pas/c/cpp)

TL:1S  ML:128MB

【Description】

GFS CLJ在玩这样一个游戏:

有一个N*M的矩阵,每个格子代表一个人。

他可以选择杀死一个人,或者让一个人活着。如果处在(i, j)位置的人被杀,得到B[i][j]的收益,否则得到W[i][j]的收益。

另外还有Q个额外任务,每个任务的形式是这样的:

如果以(x1,y1)为左上角,(x2, y2)为右下角的子矩阵表示的人全部活着/全部被杀,可以得到c的额外收益。其中,具体是要求活着还是被杀是给定的。

注意:并不是所有的任务都一定要被完成!CLJ可以自主选择一些任务去完成。

现在他想知道,怎样做才能使得收益最大。

【Input】

第一行三个整数N, M, Q

接下来一个N*M的矩阵,表示B。

接下来一个N*M的矩阵,表示W。

接下来Q行,每行6个整数,x1, y1, x2, y2, t, c。其中,如果t=0,表示这个任务要求这个子矩阵中的人全部或者,否则要求全部被杀。

【Output】

输出最大收益。

【Sample Input】

2 2 3

34 44

63 30

1 9

53 57

1 2 2 2 12843

1 1 2 1 02169

2 1 2 1 16980

【Sample Output】

9994

【Hint】

杀死所有人。

 20%:N, M <= 4

30%:Q <= 10

40%:Q <= 100

60%:Q <= 5000

另外20%:N = 1

100%:N,M<=50,Q <= 50000, 1 <= B[][], W[][]<= 100, 1 <= c <= 10000



【前言】

考场上发现是最小割并且想出了建图方式,结果发现边数太大,GG。

【正文】

经典的最小割模型。

先考虑没有Q个额外任务的情况。

对于每个点,源点向其连一条值为b[i][j]的边,汇点向其连一条w[i][j]的边,然后跑最小割即可。

证明:因为这里的人是正常人不是薛定谔,所以要么死要么活,因此可以转换为在这个网络上去掉边权和最小的边集使得网络不连通,剩余的即为最大值。

这不就是最小割嘛。

现在加入Q个额外任务,可以用同样的方式来考虑。

对于每个矩形,我们可以抽象成一个点。

1)如果要求矩形内人全死,那么源点向其连一条边权为c的边。

2)如果要求矩形内人全活,那么向汇点连一条边权为c的边。

现在考虑如何维护是否满足矩形内的人是否全死或全活。

我们可以将该点与其对应子矩形内的点连一条inf边。

因为inf边不会成为割边,所以一旦选定这个矩形,所有与其相邻的点都会被选择。

跑最小割即可。

但这样建图就会发现边数是(n*m*q),太大了。

所以我们需要优化建图。

因为是每次与其子矩阵内所有点连边,我们可以用倍增建图的思路来优化建边,通过加若干中继点的方法来减小边数。

定义st[1/0][x][y][j][k]为这个矩阵的要求为死/活时,左上角为(x,y),长为2^j,宽为2^k时对应的点数。

那么我们只要预处理出这些点,就可以把边数降为(n*m*logq)。

代码:

#include<iostream>
#include<stdio.h>
#include<string.h> 
#include<algorithm>
#include<queue>
#include<math.h>
#define maxn 1005
#define inf 0x7fffffff
using namespace std;
int n,m,q,tot=1,cnt;
int mylog[maxn*maxn];
int head[maxn*maxn],to[maxn*maxn*5],nex[maxn*maxn*5],val[maxn*maxn*5];
int b[maxn][maxn],w[maxn][maxn];
int st[2][105][105][21][21];
void add(int x,int y,int z)
{
	to[++tot]=y;val[tot]=z;nex[tot]=head[x];head[x]=tot;
	to[++tot]=x;val[tot]=0;nex[tot]=head[y];head[y]=tot;
}
int getpos(int i,int j)
{
	return (i-1)*m+j;
}
int level[maxn*maxn],iter[maxn*maxn];
void bfs(int now)
{
	queue<int>q;
	for(int i=1;i<=n;i++) level[i]=-1;
	level[now]=0;q.push(now);
	while(!q.empty())
	{
		now=q.front();q.pop();
		for(int i=head[now];i;i=nex[i])
		if(val[i]>0 && level[to[i]]==-1)
		level[to[i]]=level[now]+1,q.push(to[i]);
	}
}
int dfs(int u,int v,int flow)
{
	if(u==v) return flow;
	for(int &i=iter[u];i;i=nex[i])
	{
		if(val[i]>0 && level[to[i]]>level[u])
		{
			int res=dfs(to[i],v,min(val[i],flow));
			if(res>0)
			{
				val[i]-=res;
				val[i^1]+=res;
				return res;
			}
		}
	}
	return 0;
}
int dinic(int u,int v)
{
	int ans=0;
	while(1)
	{
		bfs(u);
		if(level[v]==-1) return ans;
		for(int i=1;i<=n;i++) iter[i]=head[i];
		int f;
		while((f=dfs(u,v,inf))>0)
		ans+=f;
	}
}
int main()
{
	mylog[2]=1;
	for(int i=3;i<maxn*maxn;i++)
	mylog[i]=mylog[i>>1]+1;
	scanf("%d%d%d",&n,&m,&q);
	tot=1;
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&b[i][j]);
			ans+=b[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&w[i][j]);
			ans+=w[i][j];
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cnt++;
			st[0][i][j][0][0]=st[1][i][j][0][0]=cnt;
		}
	}
	
	for(int i=0;(1<<i)<=n;i++)
	{
		for(int j=0;(1<<j)<=m;j++)
		if(i+j)
		{
			for(int x=1;x+(1<<i)-1<=n;x++)
			{
				for(int y=1;y+(1<<j)-1<=m;y++)
				{
					st[0][x][y][i][j]=++cnt;
					st[1][x][y][i][j]=++cnt;
				}
			}
		}
	}
	int s=cnt+q+1,e=cnt+q+2;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		add(s,getpos(i,j),w[i][j]);
		add(getpos(i,j),e,b[i][j]);
	}
	for(int i=0;(1<<i)<=n;i++)
	for(int j=0;(1<<j)<=m;j++)
	if(i+j)
	{
		for(int x=1;x+(1<<i)-1<=n;x++)
		{
			for(int y=1;y+(1<<j)-1<=m;y++)
			{
				if (i && j) 
				add(st[0][x][y][i][j],st[0][x+(1<<i)-1][y+(1<<j)-1][max(0,i-1)][max(0,j-1)],inf);
				if (j) 
				add(st[0][x][y][i][j],st[0][x][y+(1<<j)-1][max(0,i-1)][max(0,j-1)],inf);
				if (i) 
				add(st[0][x][y][i][j],st[0][x+(1<<i)-1][y][max(0,i-1)][max(0,j-1)],inf);
				add(st[0][x][y][i][j],st[0][x][y][max(0,i-1)][max(0,j-1)],inf);
				if (i && j) 
				add(st[1][x+(1<<i)-1][y+(1<<j)-1][max(0,i-1)][max(0,j-1)],st[1][x][y][i][j],inf);
				if (j) 
				add(st[1][x][y+(1<<j)-1][max(0,i-1)][max(0,j-1)],st[1][x][y][i][j],inf);
				if (i) 
				add(st[1][x+(1<<i)-1][y][max(0,i-1)][max(0,j-1)],st[1][x][y][i][j],inf);
				add(st[1][x][y][max(0,i-1)][max(0,j-1)],st[1][x][y][i][j],inf);
			}
		}
	}
	for(int i=1;i<=q;i++)
	{
		int x1,y1,x2,y2,op,c;
		scanf("%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&op,&c);
		ans+=c;
		if(op==0) add(s,cnt+i,c);
		else add(cnt+i,e,c);
		int len1=mylog[x2-x1+1],len2=mylog[y2-y1+1];
		if(op==0)
		{
			add(cnt+i,st[0][x1][y1][len1][len2],inf);
			add(cnt+i,st[0][x2-(1<<len1)+1][y1][len1][len2],inf);
			add(cnt+i,st[0][x1][y2-(1<<len2)+1][len1][len2],inf);
			add(cnt+i,st[0][x2-(1<<len1)+1][y2-(1<<len2)+1][len1][len2],inf);
		}
		else
		{
			add(st[1][x1][y1][len1][len2],cnt+i,inf);
			add(st[1][x2-(1<<len1)+1][y1][len1][len2],cnt+i,inf);
			add(st[1][x1][y2-(1<<len2)+1][len1][len2],cnt+i,inf);
			add(st[1][x2-(1<<len1)+1][y2-(1<<len2)+1][len1][len2],cnt+i,inf);
		}
	}
	n=e;
	printf("%d\n",ans-dinic(s,e));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值