题意:有一个n × m的网点状街区,在这个n × m的街区上,有l家银行。有一伙劫匪要去抢银行,抢完以后要跑,他们逃跑的路线不能重叠,逃出n × m的街区边界才算是逃跑成功。给出街区大小和各银行位置,问是否可以全部成功逃脱。
算法思路:对于给定的网格,行为S列为A,我们按行优先给所有点标号,从1到S*A。然后对每个点拆点,拆点后一个点变为两个点(那么总点数为2*S*A),在这里我把拆点后的两个点叫做“前点”和“后点”,对于坐标为(i,j)的点,拆点后“前点”的编号为u=(i-1)*A+j , “后点”的编号好v=u+S*A;
我们还要额外设置一个源点s,编号为0,一个汇点,编号为2*S*A+1。从源点s建有向边指向所有的银行,从所有网格边沿的点建有向边指向汇点t,另外网格内一个点要和它上下左右四个点建立无向边(也就是两条有向边)。数据很大,要用邻接表保存
问题就是,我们已经事先拆点了,原来的两个点(i,j)和(i,j+1)有连线那么拆点后怎么链接呢?
第一部分(一个点和它上下左右的四个点建边):从这个点的“后点”和四周的点的“前点”建有向边(因为用邻接表建图,所以所有的有向边都有反边,注意反边的容量为0)。
第二部分(源点和所有银行建边):源点s和所有银行的“前点”建有向边(还有反边)
第三部分(所有网格边沿的点和汇点建边):所有网格边沿的点的“后点”和汇点建有向边(还有反边)
因此
第一部分:两个点a,b之间会有四边有向边,一条是a点的“后点”指向b点的“前点”,一条是b点的“后点”指向a点的“前点”。这两条还附带两条反边所以一共四条
第二部分:源点和银行的“前点”有边,再附带一个反边
第三部分:网格边沿点的“后点”和汇点有边,再附带一个反边
所有边的容量都1(这样就起到了每个点只能用一次的效果),附带的反边容量当然是为0
建图后,直接EK,算最大流,最大流等于银行个数那么可以逃脱,不等(小于)就不可以
Dinic算法:#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <climits>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
int s, a, b, st, ed;
const int OF = 2500; //一开始是用s*a做的
const int INF = 0x3f3f3f3f;
const int maxn = 5005;
const int FIN = 5001; //汇点按理说s*a*2+1可以的,可这里卡BUG了
int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
struct Edge
{
int from,to,cap,flow;
Edge(int from=0,int to=0,int cap=0,int flow=0):from(from),to(to),cap(cap),flow(flow){}
};
vector<Edge> edges;
vector<int> G[maxn];
int vis[maxn], d[maxn];
int cur[maxn];
void AddEdge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
int m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int BFS()
{
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(st);
d[st] = 0;
vis[st] = 1;
while (!Q.empty())
{
int u = Q.front(); Q.pop();
for (int i = 0; i < G[u].size(); i++)
{
Edge &e = edges[G[u][i]];
if (!vis[e.to] && e.cap > e.flow)
{
vis[e.to] = 1;
d[e.to] = d[u] + 1;
Q.push(e.to);
}
}
}
return vis[ed];
}
int DFS(int u, int a)
{
if (u == ed || a == 0) return a;
int flow = 0, f;
for (int &i = cur[u]; i < G[u].size(); i++)
{
Edge &e = edges[G[u][i]];
if (d[u] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
{
e.flow += f;
edges[G[u][i]^1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int Maxflow()
{
int ans = 0;
while (BFS())
{
memset(cur, 0, sizeof(cur));
ans += DFS(st, INF);
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
st = 0, ed = FIN;
for (int i = 0; i <= FIN; i++) G[i].clear();
edges.clear();
scanf("%d %d %d", &s, &a, &b);
for(int i=1;i<=s;i++)
{
for(int j=1;j<=a;j++)
{
AddEdge((i-1)*a+j,(i-1)*a+j+OF,1);
for(int k=0;k<4;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if((x<1)||(x>s)||(y<1)||(y>a))continue;
AddEdge((i-1)*a+j+OF,(x-1)*a+y,1);
}
if((i==1)||(i==s)||(j==1)||(j==a))AddEdge((i-1)*a+j+OF,FIN,1);
}
}
for(int i=0;i<b;i++)
{
int x,y;
scanf("%d%d",&x,&y);
AddEdge(0,(x-1)*a+y,1);
}
int ans;
ans = Maxflow();
if (ans == b) printf("possible\n");
else printf("not possible\n");
}
return 0;
}
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <climits>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
int s, a, b, st, ed;
const int OF = 2500;
const int INF = 0x3f3f3f3f;
const int maxn = 5005;
const int FIN = 5001;
int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
struct Edge
{
int from,to,cap,flow;
Edge(int from=0,int to=0,int cap=0,int flow=0):from(from),to(to),cap(cap),flow(flow){}
};
vector<Edge> edges;
vector<int> G[maxn];
int vis[maxn];
int p[maxn];
void AddEdge(int from,int to,int cap)
{
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
int m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int EK()
{
int f=0;
for(;;)
{
memset(vis,0,sizeof(vis));
vis[st]=INF;
queue<int> q;
q.push(st);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int v=0; v<G[u].size(); ++v)
{
Edge& e=edges[G[u][v]];
if(!vis[e.to]&&e.flow<e.cap)
{
vis[e.to]=min(vis[u],e.cap-e.flow);
p[e.to]=G[u][v];
q.push(e.to);
}
}
}
if(vis[ed]==0) break;
for(int u=ed; u!=st; u=edges[p[u]].from)
{
edges[p[u]^1].flow-=vis[ed];
edges[p[u]].flow+=vis[ed];
}
f+=vis[ed];
}
return f;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
st = 0, ed = FIN;
for (int i = 0; i <= FIN; i++) G[i].clear();
edges.clear();
scanf("%d %d %d", &s, &a, &b);
for(int i=1;i<=s;i++)
{
for(int j=1;j<=a;j++)
{
AddEdge((i-1)*a+j,(i-1)*a+j+OF,1);
for(int k=0;k<4;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if((x<1)||(x>s)||(y<1)||(y>a))continue;
AddEdge((i-1)*a+j+OF,(x-1)*a+y,1);
}
if((i==1)||(i==s)||(j==1)||(j==a))AddEdge((i-1)*a+j+OF,FIN,1);
}
}
for(int i=0;i<b;i++)
{
int x,y;
scanf("%d%d",&x,&y);
AddEdge(0,(x-1)*a+y,1);
}
int ans;
ans = EK();
if (ans == b) printf("possible\n");
else printf("not possible\n");
}
return 0;
}