P3866 [TJOI2009] 战争游戏 题解
Solution
首先考虑如何建图。
先看建源点和汇点的原因:
- 建源点:有若干支不同的军队需要拦截,所以要将他们所在点都与源点连接,以跑网络流。
- 建汇点:有若干个通向图外的可行边界点,也要将它们都与汇点连在一起,以跑网络流。
这样一来,源点的汇点的建图方式就可以轻易得到了:
- 源点:自源点向所有军队的所在点相连,流量为 i n f inf inf。
- 汇点:将所有可以通行的图内边界点与汇点相连,流量为 i n f inf inf。
如此,解决此题的具体方式也就出来了:求最小割。
上文所述,将军队都与源点相连,可行边界点都与汇点相连,则要想阻止军队出图,就是要割去一些带权值的边,既能使源点和汇点不连通,又要使割去边的权值之和最小。
因为最大流等于最小割,所以这道题就转化为求最大流。那么上述的边权在下文就变成了流量。
再看如何建图的中间部分:
-
很明显,要将所有的可行点(即该点没有障碍物)和它四周可以到达的点连边,流量为 i n f inf inf。
-
对于空地,发现它有一个轰炸值(即轰炸所费炸yao数),特别地,对于军队所在点,轰炸值为 0。按上文所述,要想将一个点权转化为边权,也就是一条边的流量,那么方法呼之欲出:拆点。
-
所以,我们将每一个可行点拆成两个点,入点和出点,并在它们之间连边。若点为空地,则边的流量即为空地的轰炸值,这样即可限制通过该空地点的流量;若点为军队所在点,则边的流量为 i n f inf inf。
-
最后再来说入点和出点的连边:对于所有可行点,使用出点向四周可行点的入点连边;对于军队所在点,则自源点与该点的入点连边。
建图就说完了,剩下的就只有最大流板子了。
特别:注意点的编号的处理。
Code
更多详情见代码及注释。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define maxn 2005
#define maxm 40005
#define inf 2147483647
int n, m, d, nm;
int c[maxn][maxn], cntc, l[maxm], r[maxm], ans;
int s, t, p[maxn][maxn], tot;
int dep[maxm];
int hd[maxm], cur[maxm], cnt = 1;
int fx[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
struct node{
int to, nxt, w;
}e[maxm << 1];
inline void add(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].nxt = hd[u], e[cnt].w = w;
hd[u] = cnt;
e[++cnt].to = u;
e[cnt].nxt = hd[v], hd[v] = cnt;
}
inline int id(int x, int y)/*点的编号的处理*/
{
return (x - 1) * m + y;
}
inline bool bfs()
{
queue <int> q;
memset(dep, 0, sizeof dep);
dep[s] = 1, q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int v, i = hd[u]; i; i = e[i].nxt)
{
if(!dep[v = e[i].to] and e[i].w)
{
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[t] ? true : false;
}
inline int dinic(int u, int in)
{
if(u == t or !in) return in;
int out = 0;
for(int v, &i = cur[u]/*cur[u] 随着 i 的变化而变化*/; i and in/*注意判容量剩余*/; i = e[i].nxt)
/*当前弧优化*/
{
if(dep[v = e[i].to] == dep[u] + 1 and e[i].w)
{
int res = dinic(v, min(in, e[i].w));
e[i].w -= res, e[i ^ 1].w += res;
in -= res, out += res;
}
}
if(!out) dep[u] = 0;
return out;
}
int main()
{
scanf("%d %d", &n, &m);
nm = n * m, t = nm * 2 + 1;
rep(i, 1, n) rep(j, 1, m) scanf("%d", &c[i][j]);
rep(i, 1, n) rep(j, 1, m) if(c[i][j] != -1)
{
if(!c[i][j])//若该点为军队所在点
add(id(i, j), id(i, j) + nm, inf)/*拆点*/, add(s, id(i, j), inf)/*军队所在点和源点相连*/;
else /*若该点为空地*/add(id(i, j), id(i, j) + nm, c[i][j])/*拆点*/;
int nw = id(i, j) + nm;//当前入点相对应的出点
if(i == 1 or j == 1 or i == n or j == m) add(nw, t, inf);//边界点和汇点相连
rep(k, 0, 3)//向点的四周可行点连边
{
int xx = i + fx[k][0], yy = j + fx[k][1];
if(xx < 1 or yy < 1 or xx > n or yy > m or c[xx][yy] == -1) continue;
add(nw/*当前点出点*/, id(xx, yy)/*四周可行点入点*/, inf);
}
}
while(bfs())//Dinic板子
{
rep(i, s, t) cur[i] = hd[i];//当前弧优化-数组初始化
ans += dinic(s, inf);
}
printf("%d\n", ans);
return 0;
}
感谢阅读。
辛苦管理员审核,若有问题烦请指出。

浙公网安备 33010602011771号