题目大意
新克诺伦丹(Nieuw Knollendam)是一个繁华的城市,城市完全由纵向和横向的街道构成,且每个街道的交叉口都会存在一个银行。然而这不幸地吸引了很多犯罪,每天都会有很多不同的银行被抢。这不仅对银行是个问题,对罪犯也是个问题,因为他们抢完银行在被警察追得不亦乐乎时,很可能和另一个罪犯在交叉口撞到一起,从而导致逃跑的困难。
所以罪犯们决定对逃跑路径进行规划,使得每天的逃跑路径都不会使用相同的交叉口。本题试图给出这样的一个规划。
解题思路
网络流题目的难度主要在建图上。这里实质上相当于每个点都有容量,为了能套用一般的最大流算法,我们应将其拆成两点,之间用容量为1的边连接。对原图,我们应将所有交叉口看成点,所有的出口也看成点。这两者都要进行拆点。
将二维的点按(0, 0)、(0, 1)、(0, 2) …顺序编码,依次编成点0、1、2…
为了避免每次访问点都需要算出对应编码,使用一个二维数组存储每个点对应的编码。方便起见,将该矩阵中元素声明为struct,每个元素都是拆点前的一个点,struct里包含拆点后的左点l和右点r,并存储对应的值。方便起见,使用单独两个变量存储s和t的值。
拆点完毕之后是连边,需要连接如下几种类型的边:1. 每个左拆点向右拆点连接一条单向边;2. 每个交叉口的右拆点分别向上下左右四个方向的左拆点连接一条单向边;3. 源点向所有银行(银行仅仅是特殊的交叉口)的左拆点连接一条有向边;4. 所有出口的右拆点向汇点连接一条有向边。需要注意的是,这里连接的边虽然都是容量为1的单向边,但都需要再提供一条对应的容量为0的反向边,用于表示图的残留网络。
最后运行任意最大流算法即可。若流大小等于银行数则存在这样一个方案,否则不存在。
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXS = 52; // maximum number of streets/avenues
const int MAXN = MAXS*MAXS*2; // maximum number of vertices
const int MAXM = (MAXS+2)*(MAXS+2)*2 + MAXS*MAXS*16 + MAXS*MAXS*2 + MAXS*8; // maximum number of edges
const int NIL = -1;
const int INF = 99999999;
int dir[][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
struct vertex {
int l, r; // indicate a vertex's left position and right position
};
struct edge {
int u, v;
int cap, flow;
int next;
edge() {}
edge(int u, int v, int cap, int flow, int next) {
this->u = u;
this->v = v;
this->cap = cap;
this->flow = flow;
this->next = next;
}
};
int s, a, b;
edge G[MAXM];
int m; // indicate the number of edges
int head[MAXN];
int d[MAXN];
vertex H[MAXS][MAXS]; // a look-up table telling us the vertex number
int source, sink;
inline void add(int u, int v, int cap)
{
G[m] = edge(u, v, cap, 0, head[u]);
head[u] = m;
m++;
}
void initH()
{
int count = 0;
for(int i=0; i<=s+1; i++)
for(int j=0; j<=a+1; j++) {
H[i][j].l = count;
count++;
H[i][j].r = count;
count++;
}
source = 1;
sink = (s+2)*(a+2)*2-2;
}
void initG()
{
memset(head, NIL, sizeof(head));
m = 0;
// connect left vertex with right vertex = (s+2)*(a+2)*2 edges
for(int i=0; i<=s+1; i++)
for(int j=0; j<=a+1; j++) {
add(H[i][j].l, H[i][j].r, 1);
add(H[i][j].r, H[i][j].l, 0);
// unidirectional edge shall have a backward edge with a capacity of 0
// to maintain the property of residual network
}
// connect crossings with the crossings/exits around = s*a*4*4 edges
for(int i=1; i<=s; i++)
for(int j=1; j<=a; j++)
for(int k=0; k<4; k++) {
// connect a.right with b.left
add(H[i][j].r, H[i+dir[k][0]][j+dir[k][1]].l, 1);
add(H[i+dir[k][0]][j+dir[k][1]].l, H[i][j].r, 0);
// connect b.right with a.left
add(H[i+dir[k][0]][j+dir[k][1]].r, H[i][j].l, 1);
add(H[i][j].l, H[i+dir[k][0]][j+dir[k][1]].r, 0);
}
// connect source with left vertex of banks = b*2 edges
for(int i=0; i<b; i++) {
int x, y;
cin >> x >> y;
add(source, H[x][y].l, 1);
add(H[x][y].l, source, 0);
}
// connect right vertex of exits with sink = a*4+s*4 edges
for(int j=1; j<=a; j++) {
add(H[0][j].r, sink, 1);
add(sink, H[0][j].r, 0);
add(H[s+1][j].r, sink, 1);
add(sink, H[s+1][j].r, 0);
}
for(int i=1; i<=s; i++) {
add(H[i][0].r, sink, 1);
add(sink, H[i][0].r, 0);
add(H[i][a+1].r, sink, 1);
add(sink, H[i][a+1].r, 0);
}
}
// Dinic algorithm
bool Dinic_BFS()
{
bool vis[MAXN];
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(source);
d[source] = 0;
vis[source] = true;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i=head[u]; i!=-1; i=G[i].next) {
edge &e = G[i];
if(!vis[e.v] && e.cap > e.flow) {
vis[e.v] = true;
d[e.v] = d[u] + 1;
Q.push(e.v);
}
}
}
return vis[sink]; // not connected if sink is not visited
}
// f is the maximum possible flow from source to u
// if u == sink, then DFS will return the maximum flow from source to sink
int Dinic_DFS(int u, int f)
{
if(u == sink || f == 0) return f;
int result = 0;
for(int i=head[u]; i!=-1 && result < f; i=G[i].next) {
edge &e = G[i];
int temp = min(f-result, e.cap-e.flow);
// if v is deeper than u and there exists an augmenting path
if(d[u] == d[e.v]-1 && e.cap > e.flow) {
temp = Dinic_DFS(e.v, temp);
e.flow = e.flow + temp; // increase flow
G[i^1].flow = G[i^1].flow - temp; // decrease flow of backward edge
result = result + temp;
}
}
if(result == 0) d[u] = -2;
return result;
}
int Dinic()
{
int flow = 0;
while(Dinic_BFS())
while(1) {
int val = Dinic_DFS(source, INF);
if(val == 0) break;
flow += val;
}
return flow;
}
int main()
{
int p;
cin >> p;
for(int i=0; i<p; i++) {
cin >> s >> a >> b;
initH();
initG();
if(Dinic() == b)
cout << "possible" << endl;
else
cout << "not possible" << endl;
}
return 0;
}
/*
0 1 2 3 4 5 6
4 e e e e e t s = supersource
3 e c c c c c e t = supersink
2 e c c c c c e c = crossing
1 e c c c c c e e = exit
0 s e e e e e
a map of 3 streets and 5 avenues
the vertices in the map shall be divided accordingly:
0 1 2 3 4 5 6 7 8 9 10 11 12 13
4 el er el er el er el er el er t s = supersource
3 el er cl cr cl cr cl cr cl cr cl cr el er t = supersink
2 el er cl cr cl cr cl cr cl cr cl cr el er c = crossing
1 el er cl cr cl cr cl cr cl cr cl cr el er e = exit
0 s el er el er el er el er el er
all directional edges shall only go into l and go out from r
*/