Nieuw Knollendam is a very modern town. This becomes clear already when looking at the layoutof its map, which is just a rectangular grid of streets and avenues. Being an important trade centre,Nieuw Knollendam also has a lot of banks. Almost on every crossing a bank is found (although thereare never two banks at the same crossing). Unfortunately this has attracted a lot of criminals. Bankhold-ups are quite common, and often on one day several banks are robbed. This has grown into aproblem, not only to the banks, but to the criminals as well. After robbing a bank the robber tries toleave the town as soon as possible, most of the times chased at high speed by the police. Sometimestwo running criminals pass the same crossing, causing several risks: collisions, crowds of police at oneplace and a larger risk to be caught.
To prevent these unpleasant situations the robbers agreed to consult together. Every Saturday nightthey meet and make a schedule for the week to come: who is going to rob which bank on which day?For every day they try to plan the get-away routes, such that no two routes use the same crossing.Sometimes they do not succeed in planning the routes according to this condition, although they believethat such a planning should exist.
Given a grid of (s×a) and the crossings where the banks to be robbed are located, find out whetheror not it is possible to plan a get-away route from every robbed bank to the city-bounds, without usinga crossing more than once.
Input
The first line of the input contains the number of problems p to be solved.
• The first line of every problem contains the number s of streets (1 ≤ s ≤ 50), followed by thenumber a of avenues (1 ≤ a ≤ 50), followed by the number b (b ≥ 1) of banks to be robbed.
• Then b lines follow, each containing the location of a bank in the form of two numbers x (thenumber of the street) and y (the number of the avenue). Evidently 1 ≤ x ≤ s and 1 ≤ y ≤ a.
Output
The output file consists of p lines. Each line contains the text ‘possible’ or ‘not possible’. If it ispossible to plan non-crossing get-away routes, this line should contain the word: ‘possible’. If this isnot possible, the line should contain the words ‘not possible’.
Note: The picture on the right illustrates the first sample inputbelow.
Sample Input
2
6 6 10
4 1
3 2
4 2
5 2
3 4
4 4
5 4
3 6
4 6
5 6
5 5 5
3 2
2 3
3 3
4 3
3 4
Sample Output
possible
not possible
题意:给你s * a 的矩阵,每个点只能经过一次,再给你b个人,问能否让这b个人成功走出矩阵。
思路:
建立超级源点s,t,将每个盗贼的位置与超级源点s相连,flow是1.由于每个点最多只能走一次,所以要进行拆点,将点u->u',flow为1,因为逃到四面八方任意一个边界就可以了,所以将边界点与超级汇点t进行连接,flow为1,中间的路径,不是边界上的点i要跟前后左右进行建边。然后运用isap跑一遍最大流,flow==b则有解,否则无解。建模过程是重点。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define REP(I, X) for(int I = 0; I < X; ++I)
#define FF(I, A, B) for(int I = A; I <= B; ++I)
#define clear(A, B) memset(A, B, sizeof A)
#define copy(A, B) memcpy(A, B, sizeof A)
#define min(A, B) ((A) < (B) ? (A) : (B))
#define max(A, B) ((A) > (B) ? (A) : (B))
using namespace std;
typedef long long ll;
typedef long long LL;
const int oo = 0x3f3f3f3f;
const int maxE = 200000;
const int maxN = 5005;
const int maxQ = 10000;
struct Edge{
int v, c, n;
};
Edge edge[maxE];
int adj[maxN], cntE;
int Q[maxE], head, tail, inq[maxN];
int d[maxN], num[maxN], cur[maxN], pre[maxN];
int s, t, nv;
int n, m, nm;
int path[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
void addedge(int u, int v, int c){
edge[cntE].v = v; edge[cntE].c = c; edge[cntE].n = adj[u]; adj[u] = cntE++;
edge[cntE].v = u; edge[cntE].c = 0; edge[cntE].n = adj[v]; adj[v] = cntE++;
}
void REV_BFS(){
clear(d, -1);
clear(num, 0);
head = tail = 0;
d[t] = 0;
num[0] = 1;
Q[tail++] = t;
while(head != tail){
int u = Q[head++];
for(int i = adj[u]; ~i; i = edge[i].n){
int v = edge[i].v;
if(~d[v]) continue;
d[v] = d[u] + 1;
num[d[v]]++;
Q[tail++] = v;
}
}
}
int ISAP(){
copy(cur, adj);
REV_BFS();
int flow = 0, u = pre[s] = s, i;
while(d[s] < nv){
if(u == t){
int f = oo, neck;
for(i = s; i != t; i = edge[cur[i]].v){
if(f > edge[cur[i]].c){
f = edge[cur[i]].c;
neck = i;
}
}
for(i = s; i != t; i = edge[cur[i]].v){
edge[cur[i]].c -= f;
edge[cur[i] ^ 1].c += f;
}
flow += f;
u = neck;
}
for(i = cur[u]; ~i; i = edge[i].n) if(edge[i].c && d[u] == d[edge[i].v] + 1) break;
if(~i){
cur[u] = i;
pre[edge[i].v] = u;
u = edge[i].v;
}
else{
if(0 == (--num[d[u]])) break;
int mind = nv;
for(i = adj[u]; ~i; i = edge[i].n){
if(edge[i].c && mind > d[edge[i].v]){
mind = d[edge[i].v];
cur[u] = i;
}
}
d[u] = mind + 1;
num[d[u]]++;
u = pre[u];
}
}
return flow;
}
void work(){
int x, y, b;
clear(adj, -1);
cntE = 0;
scanf("%d%d%d", &n, &m, &b);
nm = n * m;
s = nm * 2;//之前0-nm*2-1是图中的坐标的点们,所以规定nm*2是s点,nm*2+1是点t
t = s + 1;
nv = t + 1; //因为是d[s] < nv ,所以要加一
REP(x, n) REP(y, m){
int xy = x * m + y;
addedge(xy, xy + nm, 1);//拆点操作!!!必不可少!!!为了保证每个点只走一次
if(x == 0 || y == 0 || x == n - 1|| y == m - 1)//在一圈的边界上,建立与t的边,flow为1
addedge(nm + xy, t, 1);
else REP(k, 4)
{
int nx = x + path[k][0];
int ny = y + path[k][1];
addedge(xy + nm, nx * m + ny, 1);//上下左右进行建边
}
}
REP(i, b)
{
scanf("%d%d", &x, &y);
--x; --y;//因为是从0开始的
addedge(s, x * m + y, 1);//将盗贼的位置和源点进行建边,flow为1
}
printf(b == ISAP() ? "possible\n" : "not possible\n");
}
int main(){
int T;
for(scanf("%d", &T); T; --T) work();
return 0;
}
文章参考:
http://blog.csdn.net/u013368721/article/details/27802709