Description:
有一个 n×mn×m的地图, 地图上的每一个位置可以是空地, 炮塔或是敌人. 你需要操纵炮塔消灭敌人.对于每个炮塔都有一个它可以瞄准的方向, 你需要在它的瞄准方向上确定一个它的攻击位置,当然也可以不进行攻击. 一旦一个位置被攻击, 则在这个位置上的所有敌人都会被消灭.保证对于任意一个炮塔, 它所有可能的攻击位置上不存在另外一个炮塔.定义炮弹的运行轨迹为炮弹的起点和终点覆盖的区域. 你需要求出一种方案, 使得没有两条炮弹轨迹相交.
Solution:
吐槽:
看起来就是网络流,但是考试的时候并没有想出来利用最大值的方法,于是放弃了类似切糕的做法,写了一个费用流,结果发现导弹能转弯,于是内心崩溃,第二题也没码出来。
首先转换为能够用最小割解决的问题,那么对于每行每列我们求出最大值,相邻点之间的容量为最大值-权值,这样就能表示出选哪个点。然后考虑如何不能相交,源连向横着射击的点,竖着射击的点连向汇。之后利用了类似切糕的想法,把每个交点拆成横竖两点,之间连inf,这样就保证了在这个交点前肯定会选一个权值,并且只有一个射击塔会选。
感觉最妙的地方还是在于利用最大值来解决这个问题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55, maxp = 55 * 55 * 3, inf = 1e9;
struct edge {
int nxt, to, f;
} e[2000005];
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
#define id(x, y, z) z * n * m + (x - 1) * m + y
int n, m, ans, cnt = 1, source, sink;
int h[maxp], iter[maxp], d[maxp], a[maxn][maxn];
void link(int u, int v, int f) {
e[++cnt].nxt = h[u];
h[u] = cnt;
e[cnt].to = v;
e[cnt].f = f;
}
void insert(int u, int v, int f) {
link(u, v, f);
link(v, u, 0);
}
bool bfs() {
queue<int> q;
q.push(source);
memset(d, -1, sizeof(d));
d[source] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = h[u]; i; i = e[i].nxt) {
if(d[e[i].to] == -1 && e[i].f) {
d[e[i].to] = d[u] + 1;
q.push(e[i].to);
}
}
}
return d[sink] != -1;
}
int dfs(int u, int delta) {
if(u == sink) {
return delta;
}
int ret = 0;
for(int &i = iter[u]; i; i = e[i].nxt) {
if(d[e[i].to] == d[u] + 1 && e[i].f) {
int x = dfs(e[i].to, min(delta, e[i].f));
e[i].f -= x;
e[i ^ 1].f += x;
delta -= x;
ret += x;
if(!delta) {
break;
}
}
}
return ret;
}
int dinic() {
int ret = 0;
while(bfs()) {
for(int i = source; i <= sink; ++i) {
iter[i] = h[i];
}
ret += dfs(source, inf);
}
return ret;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
scanf("%d", &a[i][j]);
}
}
sink = n * m * 2 + 1;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
if(a[i][j] < 0) {
int mx = 0, x = i, y = j, t = -a[i][j] - 1;
while(1) {
x += dx[t];
y += dy[t];
if(x < 1 || x > n || y < 1 || y > m) {
break;
}
mx = max(mx, a[x][y]);
}
ans += mx;
if(t < 2) {
insert(source, id(i, j, 0), inf);
} else {
insert(id(i, j, 1), sink, inf);
}
x = i;
y = j;
while(1) {
int xx = x, yy = y;
x += dx[t];
y += dy[t];
if(x < 1 || x > n || y < 1 || y > m) {
break;
}
if(t < 2) {
insert(id(xx, yy, 0), id(x, y, 0), mx - max(0, a[xx][yy]));
} else {
insert(id(x, y, 1), id(xx, yy, 1), mx - max(0, a[xx][yy]));
}
}
} else {
insert(id(i, j, 0), id(i, j, 1), inf);
}
}
}
printf("%d\n", ans - dinic());
return 0;
}