题面:
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());
}