题目大意
给定一个$n*m$的网格图上
有些格子内存在炮台,每个炮台都被指定了上下左右四个方向中的某一个方向,并可以选定这个方向上的一个格子发动一次攻击。
保证没有任何炮台能攻击另一个炮台,同时炮台可以不攻击。
有些格子内存在数量为$a[i][j]$的敌人,攻击一个格子能击杀所有格子内的敌人。
定义每个炮台与它攻击的目标之间的格子为攻击轨迹。一个合法的攻击方案满足,任意两个炮台之间的攻击轨迹不相交。
求击杀敌人数最多的一种合法方案击杀的敌人数。
$n,m leq 50,a[i][j] leq 1000$
题解
当你做一道题,发现什么都想不到的时候,可以试一试网络流。-——出题人
考虑将格子上的权值取负,使用最小割模型。
假设不考虑攻击轨迹不相交的限制,那么可以用如下方法建图:
将每个格子拆成两个点,分别为横点和竖点。
对于每个横着的炮台,使源点连向它,将其覆盖的格子的横点串成一条链,流量为每个格子的敌人数目,最后令链上最后一个边界格子连向汇点。
对于每个竖着的炮台,使它连向汇点,同样将覆盖的格子串成一条链,但是使用的是竖点,且方向与横着时相反,并令源点向链的起点,也就是边界格子连边。
这样,建出的图的一个割对应一组攻击方案。
考虑不能相交的限制。
考虑在对应相交位置处的横点向竖点连一条$Inf$边。
此时,若横点割掉相交处的格子对应的边,则竖点割掉相交处格子及之前的边并不能使图不连通,因为横着的$Inf$边依旧可以流入。
于是,竖点只能割掉之后的边。由于竖点的边是反着建的,因此剩余可选择的边集对应原图上不相交的所有方案。
同理,若竖点割掉相交处的格子对应的边,则横点只能割掉相交处的格子对应的边之前的边,否则之间的边处的流量会通过$Inf$边流向竖点的汇点。
这个模型的来源是HNOI的切糕一题。
于是跑一边最小割即可~
代码:
(-1、-2、-3、-4分别对应上下左右)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int N=59;
const int mint=1000;
int n,m,s,t;
int g[N][N],h[N][N];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
inline bool in(int x,int y)
{
return 1<=x && x<=n && 1<=y && y<=m;
}
namespace flow
{
const int P=N*N*5;
const int M=P*3;
int to[M<<1],nxt[M<<1],w[M<<1],beg[P],tot=1;
int dis[P],q[P];
inline void adde(int u,int v,int c)
{
to[++tot]=v;
nxt[tot]=beg[u];
w[tot]=c;
beg[u]=tot;
}
inline void add(int u,int v,int c)
{
//printf("%d %d %d
",u,v,mint-c);
adde(u,v,c);adde(v,u,0);
}
inline bool bfs()
{
memset(dis,-1,sizeof(dis));
q[1]=s;dis[s]=0;
for(int l=1,r=1,u=q[l];l<=r;u=q[++l])
for(int i=beg[u];i;i=nxt[i])
if(w[i]>0 && !(~dis[to[i]]))
dis[to[i]]=dis[u]+1,q[++r]=to[i];
return ~dis[t];
}
inline int dfs(int u,int flow)
{
if(u==t || !flow)return flow;
int cost=0;
for(int i=beg[u],f;i;i=nxt[i])
if(w[i]>0 && dis[to[i]]==dis[u]+1)
{
f=dfs(to[i],min(flow-cost,w[i]));
w[i]-=f;w[i^1]+=f;cost+=f;
if(cost==flow)break;
}
if(!cost)dis[u]=-1;
return cost;
}
inline int dinic()
{
int ret=0;
while(bfs())
ret+=dfs(s,2e9);
return ret;
}
}
inline int pt(int a,int b,int d)
{
return (((a-1)*m+b)<<1)-d;
}
int main()
{
freopen("cti.in","r",stdin);
freopen("cti.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
g[i][j]=read();
int totcnt=0;
s=n*m*2+1;t=s+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(g[i][j]<=-3)
{
flow::add(s,pt(i,j,1),mint);
int dir=g[i][j]+4,k;
for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
{
int cx=i+k*dx[dir],cy=j+k*dy[dir];
int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
flow::add(pt(lx,ly,1),pt(cx,cy,1),mint-max(0,g[cx][cy]));
h[cx][cy]=dir+1;
}
k--;
int cx=i+k*dx[dir],cy=j+k*dy[dir];
flow::add(pt(cx,cy,1),t,1e8);
totcnt++;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(-2<=g[i][j] && g[i][j]<=-1)
{
flow::add(pt(i,j,0),t,mint);
int dir=g[i][j]+4,k;
for(k=1;in(i+k*dx[dir],j+k*dy[dir]);k++)
{
int cx=i+k*dx[dir],cy=j+k*dy[dir];
int lx=i+(k-1)*dx[dir],ly=j+(k-1)*dy[dir];
flow::add(pt(cx,cy,0),pt(lx,ly,0),mint-max(0,g[cx][cy]));
if(h[cx][cy])
flow::add(pt(cx-dx[h[cx][cy]-1],cy-dy[h[cx][cy]-1],1),pt(lx,ly,0),1e8);
}
k--;
int cx=i+k*dx[dir],cy=j+k*dy[dir];
flow::add(s,pt(cx,cy,0),1e8);
totcnt++;
}
printf("%d
",totcnt*mint-flow::dinic());
return 0;
}