题目描述
有一个 (n imes m)的网格,每个格子里面可能有一些炮塔,或者有几个人。
每个炮塔可以在给定的方向(上下左右)上选一个点作为它的攻击位置,然后消灭这个格子里面的所有人。当然也可以不进行攻击。
要求两个炮弹的飞行轨迹不能相交。
问你最多能打死多少个人。
保证不存在一个炮塔可以攻击另一个炮塔的情况。
(n,mleq 50,) 每个格子的人数 (< 1000)
题解
先忽略"要求两个炮弹的飞行轨迹不能相交"这个条件。
那么可以把每个炮塔能攻击到的格子找出来,连成一条链,容量为前面这个格子的人数。
但这样求出来的是最小值。
用一个大整数去减掉每个数作为边权就好了。
现在有"要求两个炮弹的飞行轨迹不能相交"这个条件。
参考 HNOI2013这道题。
可以把朝向为左右的炮塔的链的方向反过来,然后在相交的位置上连一条(竖着的炮塔那一列对应的格子 ( o) 横着的炮塔那一行对应的格子)的边,容量为 (infty)。
为什么这是对的?
如果竖着的炮塔攻击的点超过了两条链的交点,横着的炮塔攻击的点也超过了两条链的交点,那么那条容量为 (infty) 的边就一定会有流量经过,然后会沿着 (S o) 交点 ( o T) 流到终点。所以这样建图就保证了如果竖着的炮塔攻击的点超过了两条链的交点,那么横着的炮塔攻击的点就不会超过了两条链的交点。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c,b=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c=='-')
{
c=getchar();
b=1;
}
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return b?-s:s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
const int inf=0x7fffffff;
namespace flow
{
int v[100010];
int c[100010];
int t[100010];
int h[100010];
int cnt;
void add(int x,int y,int a)
{
cnt++;
v[cnt]=y;
c[cnt]=a;
t[cnt]=h[x];
h[x]=cnt;
}
int e[100010];
int d[100010];
int op(int x)
{
return ((x-1)^1)+1;
}
int S,T,num;
queue<int> q;
void bfs()
{
memset(d,-1,sizeof d);
q.push(T);
d[T]=0;
while(!q.empty())
{
int x=q.front();
q.pop();
e[d[x]]++;
for(int i=h[x];i;i=t[i])
if(c[op(i)]&&d[v[i]]==-1)
{
d[v[i]]=d[x]+1;
q.push(v[i]);
}
}
}
int cur[100010];
int dfs(int x,int flow)
{
if(x==T)
return flow;
int s=0;
for(int &i=cur[x];i;i=t[i])
if(c[i]&&d[v[i]]==d[x]-1)
{
int u=dfs(v[i],min(flow,c[i]));
c[i]-=u;
c[op(i)]+=u;
flow-=u;
s+=u;
if(!flow)
return s;
}
e[d[x]]--;
if(!e[d[x]])
e[S]=num;
d[x]++;
e[d[x]]++;
cur[x]=h[x];
return s;
}
int solve()
{
bfs();
int ans=0;
memcpy(cur,h,sizeof h);
while(d[S]>=0&&d[S]<=num-1)
ans+=dfs(S,inf);
return ans;
}
}
void add(int x,int y,int z)
{
flow::add(x,y,z);
flow::add(y,x,0);
}
int n,m;
int a[100][100];
int b[100][100];
int main()
{
open("c");
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
flow::S=1;
flow::T=2;
flow::num=2;
int cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]==-3)
{
if(j==1)
continue;
cnt++;
for(int k=j;k>=1;k--)
b[i][k]=++flow::num;
add(flow::S,b[i][1],1000-a[i][1]);
for(int k=1;k<j;k++)
add(b[i][k],b[i][k+1],1000-a[i][k+1]);
add(b[i][j],flow::T,1000);
}
else if(a[i][j]==-4)
{
if(j==m)
continue;
cnt++;
for(int k=j;k<=m;k++)
b[i][k]=++flow::num;
add(flow::S,b[i][m],1000-a[i][m]);
for(int k=j;k<m;k++)
add(b[i][k+1],b[i][k],1000-a[i][k]);
add(b[i][j],flow::T,1000);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]==-1)
{
if(i==1)
continue;
cnt++;
for(int k=1;k<=i;k++)
{
int v=++flow::num;
if(b[k][j])
add(v,b[k][j],inf);
b[k][j]=v;
}
add(flow::S,b[i][j],1000);
add(b[i][j],b[i-1][j],1000);
for(int k=i-1;k>1;k--)
add(b[k][j],b[k-1][j],1000-a[k][j]);
add(b[1][j],flow::T,1000-a[1][j]);
}
else if(a[i][j]==-2)
{
if(i==n)
continue;
cnt++;
for(int k=i;k<=n;k++)
{
int v=++flow::num;
if(b[k][j])
add(v,b[k][j],inf);
b[k][j]=v;
}
add(flow::S,b[i][j],1000);
add(b[i][j],b[i+1][j],1000);
for(int k=i+1;k<n;k++)
add(b[k][j],b[k+1][j],1000-a[k][j]);
add(b[n][j],flow::T,1000-a[n][j]);
}
int ans=flow::solve();
ans=cnt*1000-ans;
printf("%d
",ans);
return 0;
}