文章目录
R e s u l t Result Result
H y p e r l i n k Hyperlink Hyperlink
D e s c r i p t i o n Description Description
一个 n × m n\times m n×m的网格上有若干个炮台,每个炮台有一个固定的方向,它可以选择打掉这个方向上任意一个点,并获得这个点上的价值(每个点的至多被获取一次价值,保证一个炮台打不到另一个炮台)
若炮台路径不相交,求最大代价
数据范围: n , m ≤ 50 n,m\leq 50 n,m≤50
S o l u t i o n Solution Solution
这么小的数据范围不是 d p dp dp就是网络流啦(显然考虑网络流嘛,毕竟这是一个取舍问题)
但是正向求貌似很难搞炮台路径相交这个恶心的点,我们考虑用可能的答案减去不合法的
显然,若我们不考虑路径相交,答案就是每个炮台在攻击路径上取个最大值就好了
若考虑路径相交,说明每台炮不能那么舒服的打到价值最高的点,而是考虑你打了某个点会对答案造成的影响
显然我们是可以预处理出来每个炮台打到每个点的价值,然后将路径看做从纵向的走向横向的(就是划分出来源点和汇点分别相连的点)
由于路径不能相交,换句话说就是没有通路,自然想到了最小割
不过这样做处理不了某种恶心人的情况,就是虽然我这条是通路,但实际上是合法的,如下图
针对左图这种情况,我们显然可以选择割掉右图中的这三条边就可以合法
但是在网络流中,它还会多割一条边使得图不连通,就恶心到了我们
解决的方法也很简单,考虑拆点使得横纵分开即可
时间复杂度: O ( f l o w ( n 2 m 2 ) ) O(flow(n^2m^2)) O(flow(n2m2))
C o d e Code Code
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define p(i,j) (((i-1)*m+j)*2)
#define q(i,j) (((i-1)*m+j)*2-1)
#define N 5100
using namespace std;int n,m,a[51][51],ans,s,t,sum;
struct node{int next,to,w;}e[N<<2];
int l[N],tot,d[N];
bool vis[N];
inline void add(int u,int v,int w)
{
e[tot]=(node){l[u],v,w};l[u]=tot++;
e[tot]=(node){l[v],u,0};l[v]=tot++;
return;
}
inline bool bfs()
{
memset(d,-1,sizeof(d));
queue<int>q;d[s]=0;q.push(s);
while(q.size())
{
int x=q.front();q.pop();
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(e[i].w&&d[y]==-1)
{
d[y]=d[x]+1;
q.push(y);
if(y==t) return true;
}
}
}
return false;
}
int dfs(int x,int flow)
{
if(x==t||!flow) return flow;
int rest=0,k=0,f=0;
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(d[x]+1==d[y]&&e[i].w)
{
f=dfs(y,min(flow-rest,e[i].w));
if(!f) d[y]=-1;
e[i].w-=f;rest+=f;e[i^1].w+=f;
}
}
if(!rest) d[x]=-1;
return rest;
}
void dinic()
{
while(bfs()) sum-=dfs(s,1e9);
return;
}
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
signed main()
{
memset(l,-1,sizeof(l));
n=read();m=read();s=0;t=n*m*2+1;
for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) a[i][j]=read(),add(p(i,j),q(i,j),1e9);
for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) if(a[i][j]<0)
{
int mx=0;
if(a[i][j]==-1)
{
add(s,p(i,j),1e9);
for(register int k=i;k>=1;k--) mx=max(a[k][j],mx);
for(register int k=i;k>=2;k--) add(p(k,j),p(k-1,j),mx-max(a[k][j],0));
}
if(a[i][j]==-2)
{
add(s,p(i,j),1e9);
for(register int k=i;k<=n;k++) mx=max(a[k][j],mx);
for(register int k=i;k<n;k++) add(p(k,j),p(k+1,j),mx-max(a[k][j],0));
}
if(a[i][j]==-3)
{
add(q(i,j),t,1e9);
for(register int k=j;k>=1;k--) mx=max(a[i][k],mx);
for(register int k=j;k>=2;k--) add(q(i,k-1),q(i,k),mx-max(a[i][k],0));
}
if(a[i][j]==-4)
{
add(q(i,j),t,1e9);
for(register int k=j;k<=m;k++) mx=max(a[i][k],mx);
for(register int k=j;k<m;k++) add(q(i,k+1),q(i,k),mx-max(a[i][k],0));
}
sum+=mx;
}
dinic();
printf("%lld",sum);
}