题目大意:
有一个n × m的网点状街区,在这个n × m的街区上,有l家银行。有一伙劫匪要去抢银行,抢完以后要跑,他们逃跑的路线不能重叠,逃出n × m的街区边界才算是逃跑成功。给出街区大小和各银行位置,问是否可以全部成功逃脱。
思路:
构造一个超级源点,与被抢劫的银行相连接,然后边界与超级汇点相连接,每个十字路口的点都要进行拆点,它们之间的容量为1,因为每个十字路口只能有一个人走,十字路口的点还要和它四面八方的点进行连接,它四面八方的点可能是边界,这样就可以构成一个图。
最后判断最大流是否等于被抢银行的数量,如果是的话,就可以,如果不是的话,就不可以。
注意:要进行拆点。
代码:
#include <iostream>
using namespace std;
#include <cstring>
#include <stdio.h>
#include <cstring>
#include <queue>
const int MAX = 30010;
const int MAXN = 5010;
const int INF =0x3f3f3f3f;
struct node {
int u,v,ne,flow,cap;
}e[MAX];
int dir[4][2] = {1,0,-1,0,0,1,0,-1};
int pre[MAXN];
int p[MAXN];
int mr[MAXN];
int edgenum;
int n,m,num,s,t,F;
void add(int u,int v) {
e[edgenum].u = u;
e[edgenum].v = v;
e[edgenum].flow = 0;
e[edgenum].cap = 1;
e[edgenum].ne = pre[u];
pre[u] = edgenum++;
e[edgenum].v = u;
e[edgenum].u = v;
e[edgenum].flow = 0;
e[edgenum].cap = 0;
e[edgenum].ne = pre[v];
pre[v] = edgenum++;
}
void init() {
memset(pre,-1,sizeof(pre));
edgenum = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int u = (i - 1) * m + j;
int e = u + n * m;
add(u,e);
for(int k = 0; k < 4; k++) {
int x = i + dir[k][0];
int y = j + dir[k][1];
if(x >= 1 && x <= n && y >= 1 && y <= m) {
int uu = (x - 1) * m + y;
add(e,uu);
}
else
add(e,t);
}
}
}
int u,v;
for(int i = 1; i <= num; i++) {
scanf("%d %d",&u,&v);
add(s,(u - 1) * m + v);
}
}
void EK() {
queue<int> q;
F = 0;
// memset(p,0,);
while(1) {
memset(p,-1,sizeof(0));
memset(mr,0,sizeof(mr));
mr[0] = INF;
q.push(0);
while(!q.empty()) {
int u = q.front();
q.pop();
for(int k = pre[u]; k != -1; k = e[k].ne) {
int v = e[k].v;
if(!mr[v] && e[k].cap > e[k].flow) {
p[v] = k;
q.push(v);
mr[v] = min(mr[u],e[k].cap - e[k].flow);
}
}
}
if(mr[t] == 0) break;
for(int k = p[t];k != -1; k = p[e[k^1].v]) {
e[k].flow += mr[t];
e[k^1].flow -= mr[t];
}
F += mr[t];
}
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&n,&m,&num);
s = 0;
t = n * m * 2 + 1;
init();
EK();
if(F == num)
printf("possible\n");
else
printf("not possible\n");
}
return 0;
}