BZOJ 4657: tower【路径最小割约束】

题面:

Description
Nick最近在玩一款很好玩的游戏,游戏规则是这样的:
有一个n*m的地图,地图上的每一个位置要么是空地,要么是炮塔,要么是一些BETA狗,Nick需
要操纵炮塔攻击BETA狗们。
攻击方法是:对于每个炮塔,游戏系统已经给出它可以瞄准的方向(上下左右其中一个),Nick需要
选择它的攻击位置,每一个炮塔只能够攻击一个位置,炮塔只能够向着它的瞄准方向上的某个位置发
动攻击,当然炮塔也可以不进行攻击。炮塔威力强大,它可以且仅可以消灭目标位置上所有的BETA狗。
出于安全考虑,游戏系统已经保证不存在一个炮塔能够瞄准另外一个炮塔,即对于任意一个炮
塔,它所有可能的攻击位置上不存在另外一个炮塔。而且,如果把炮塔的起点和终点称为炮弹的运行
轨迹,那么系统不允许两条轨迹相交(包括起点和终点)。
现在,选定目标位置以后,每一个炮塔同时开炮,你要告诉Nick,他最多可以干掉多少BETA狗。
Input
第一行两个正整数n,m,表示地图的规模。
接下来礼行,每行m个整数,0表示空地,-1,-2,一3,-4分别表示瞄准上下左右的炮塔,若为正整
数p,则表示该位置有p个BETA狗。
n,m <= 50,每个位置的BETA狗数量不超过999个,保证不存在任意一个炮塔能够瞄准另外一个炮塔
Output
一个正整数,表示Nick最多可以干掉几个BETA狗
Sample Input
3 2
0 9
-4 3
0 -1
Sample Output
9

题目分析:

一开始想的是费用流的做法,每个位置的beta狗拆成能够打到它的炮塔个数的点,然后加点约束连边,但是这样同一个beta狗可能会被打多次,约束是不完整的。

回顾一下题面,要求的是两条轨迹不能相交,这就是一个约束条件,但是平常做的最小割二元关系模型并不能很好地表示这个约束,我们考虑回归最小割的本质意义,表示的是一条从S集到T集的路径中必然有一条边被割掉,这和两条轨迹不能相交其实有异曲同工之妙。
由于相交的只能是横炮塔和竖炮塔,可以把它看成是不能有从竖炮塔到横炮塔的路径
把图中的每个点拆成竖点和横点,竖点向对应的横点连inf的边
从S向竖炮塔连inf的边,炮塔可以打到的相邻两竖点之间连边,
从横炮塔向T连inf的边,炮塔可以打到的相邻两横点之间连边(与竖炮塔的方向相反)。
这样就表示出了一条路径。
再考虑路径中的边容量应该为多少,
把炮塔能够打到的beta狗中的最大值记为mx,那么炮塔路径最多只会延伸到第一个mx,再往后只会更劣。我们先把每个炮塔的mx加入ans中。
对于竖炮塔的一条边u->v,割掉它表示炮塔打到了u点,那么ans就会损失掉mx-u点的beta狗,于是这条边的容量为mx-u点的beta狗。
对于横炮塔的一条边u->v,割掉它表示炮塔打到了v点,那么ans就会损失掉mx-v点的beta狗,于是这条边的容量为mx-v点的beta狗。

答案就是ans-最小割。

Code:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 5005
#define maxm 30005
#define p(i,j) (i-1)*m+j
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,S,T,sum;
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],cap[maxm],tot=1;
inline void line(int x,int y,int z){
	nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cap[tot]=z;
	nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,cap[tot]=0;
}
namespace Maxflow{
	int d[maxn],vd[maxn],sz;
	int aug(int u,int augco){
		if(u==T) return augco;
		int need=augco,delta;
		for(int i=cur[u];i;i=nxt[i]) if(cap[i]&&d[u]==d[to[i]]+1){
			delta=aug(to[i],min(cap[i],need));
			cap[i]-=delta,cap[i^1]+=delta;
			if(!(need-=delta)||d[S]==sz) return augco-need;
		}
		cur[u]=fir[u];
		if(!(--vd[d[u]])) d[S]=sz;
		vd[++d[u]]++;
		return augco-need;
	}
	int SAP(){
		memset(d,0,(T+1)<<2);
		memset(vd,0,(T+1)<<2);
		int flow=0;sz=T+1;
		while(d[S]<sz) flow+=aug(S,inf);
		return flow;
	}
}
int a[55][55];
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int main()
{
	scanf("%d%d",&n,&m);S=0,T=2*n*m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]<0){
		int dr=-a[i][j]-1,mx=0,u,v;
		a[i][j]=0;
		for(u=i+dx[dr],v=j+dy[dr];1<=u&&u<=n&&1<=v&&v<=m;u+=dx[dr],v+=dy[dr]) mx=max(mx,a[u][v]);
		sum+=mx;
		if(dr<=1){
			line(S,p(i,j),inf);
			for(u=i,v=j;a[u][v]!=mx;u+=dx[dr],v+=dy[dr])
				line(p(u,v),p(u+dx[dr],v+dy[dr]),mx-a[u][v]);
		}
		else{
			line(p(i,j)+n*m,T,inf);
			for(u=i,v=j;a[u][v]!=mx;u+=dx[dr],v+=dy[dr]) 
				line(n*m+p(u+dx[dr],v+dy[dr]),n*m+p(u,v),mx-a[u][v]);
		}
	}
	for(int i=1;i<=n*m;i++) line(i,n*m+i,inf);
	printf("%d\n",sum-Maxflow::SAP());
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值