4657: tower
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 59 Solved: 28
[ Submit][ Status][ Discuss]
Description
Nick最近在玩一款很好玩的游戏,游戏规则是这样的:
有一个n*m的地图,地图上的每一个位置要么是空地,要么是炮塔,要么是一些BETA狗,Nick需
要操纵炮塔攻击BETA狗们。
攻击方法是:对于每个炮塔,游戏系统已经给出它可以瞄准的方向(上下左右其中一个),Nick需要
选择它的攻击位置,每一个炮塔只能够攻击一个位置,炮塔只能够向着它的瞄准方向上的某个位置发
动攻击,当然炮塔也可以不进行攻击。炮塔威力强大,它可以且仅可以消灭目标位置上所有的BETA狗。
出于安全考虑,游戏系统已经保证不存在一个炮塔能够瞄准另外一个炮塔,即对于任意一个炮
塔,它所有可能的攻击位置上不存在另外一个炮塔。而且,如果把炮塔的起点和终点称为炮弹的运行
轨迹,那么系统不允许两条轨迹相交f包括起点和终点)。
现在,选定目标位置以后,每一个炮塔同时开炮,你要告诉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
0 9
-4 3
0 -1
Sample Output
9
HINT
Source
对于每个纵向发射的炮弹,定义它的方向是从炮台出发到目标
对于每个横向发射的炮弹,定义它的方向是从目标出发到炮台
考虑两条相交的炮弹轨迹,就能画成这样的形式
那么就可以看成一条x->y的路径
所以一个合法的方案必然不存在任何这样的路径
先将每条路径上最大的BETA狗数量统计出来,加起来作为答案
考虑使用最小割将不合法的路径剔除
对于一条有向边(u,v),如果割掉它,就当做让炮塔攻击了点u
那么每个纵向炮塔向出发的方向连边,每个横向炮塔向进入的方向连边
不过这样会出现一个小BUG
此图虽然出现了一条x->y的路径,但是有一个合法方案可以与之对应,即每个点都打到面前的板上
出现这种情况的原因,是这条路径连续拐了两个弯,那么不允许出现连续拐两个弯的局面
把原图中每个点拆成纵点和横点,每个纵点向对应的横点连INF的边
纵点和纵点之间,横点和横点之间再连边即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;
const int maxn = 5050;
const int INF = ~0U>>1;
const int maxm = 4E5 + 40;
const int dx[4] = {-1,1,0,0};
const int dy[4] = {0,0,-1,1};
struct E{
int to,cap,flow; E(){}
E(int to,int cap,int flow): to(to),cap(cap),flow(flow){}
}edgs[maxm];
int n,m,tot,s,t,Ans,cnt,A[55][55],B[55][55],w[55][55],cur[maxn],L[maxn];
queue <int> Q;
vector <int> v[maxn];
inline void Add(int x,int y,int cap)
{
v[x].push_back(cnt); edgs[cnt++] = E(y,cap,0);
v[y].push_back(cnt); edgs[cnt++] = E(x,0,0);
}
void Build()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (w[i][j] >= 0) continue;
int dir = -(w[i][j] + 1),Max = w[i][j] = 0;
for (int x = i + dx[dir],y = j + dy[dir]; ;)
{
if (x < 1 || x > n || y < 1 || y > m) break;
Max = max(Max,w[x][y]); x += dx[dir]; y += dy[dir];
}
Ans += Max;
if (dir <= 1)
{
Add(s,A[i][j],INF);
for (int x = i + dx[dir],y = j + dy[dir]; ;)
{
if (x < 1 || x > n || y < 1 || y > m) break;
Add(A[x - dx[dir]][y - dy[dir]],A[x][y],Max - w[x - dx[dir]][y - dy[dir]]);
x += dx[dir]; y += dy[dir];
}
}
else
{
Add(B[i][j],t,INF);
for (int x = i + dx[dir],y = j + dy[dir]; ;)
{
if (x < 1 || x > n || y < 1 || y > m) break;
Add(B[x][y],B[x - dx[dir]][y - dy[dir]],Max - w[x - dx[dir]][y - dy[dir]]);
x += dx[dir]; y += dy[dir];
}
}
}
}
inline bool BFS()
{
for (int i = 1; i <= tot; i++) L[i] = 0;
L[s] = 1; Q.push(s);
while (!Q.empty())
{
int k = Q.front(); Q.pop();
for (int i = 0; i < v[k].size(); i++)
{
E e = edgs[v[k][i]];
if (e.cap == e.flow || L[e.to]) continue;
L[e.to] = L[k] + 1; Q.push(e.to);
}
}
return L[t];
}
inline int Dinic(int x,int a)
{
if (x == t) return a; int flow = 0;
for (int &i = cur[x]; i < v[x].size(); i++)
{
E &e = edgs[v[x][i]];
if (e.cap == e.flow || L[e.to] != L[x] + 1) continue;
int f = Dinic(e.to,min(a,e.cap - e.flow));
if (!f) continue; flow += f; e.flow += f;
edgs[v[x][i]^1].flow -= f; a -= f;
if (!a) return flow;
}
if (!flow) L[x] = -1; return flow;
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
A[i][j] = ++tot; B[i][j] = ++tot;
scanf("%d",&w[i][j]); Add(A[i][j],B[i][j],INF);
}
s = ++tot; t = ++tot; Build();
int MinCut = 0;
while (BFS())
{
for (int i = 1; i <= tot; i++) cur[i] = 0;
MinCut += Dinic(s,INF);
}
cout << Ans - MinCut << endl;
return 0;
}