【网络流24题】火星探险问题

Posted by WHZ0325 on 2019-05-03, Viewed times

题目描述

给定 $p\times q$ 个位置,每个位置可能平坦无障碍、有障碍或有石块,$n$ 个探测车从左上角 $(1,1)$ 出发,每次只能往右或往下走,到达 $(q,p)$,输出使采集岩石最多时每辆车的行走方案。

$n,p,q\le 35$。

算法分析

难点在于如何表示取走石块,将每个点拆点为 $x$ 和 $x’$,$x\to x’$ 连接一条容量为 $1$ 权值为 $-1$ 的边,$x\to x’$ 连接一条容量为无穷大权值为 $0$ 的边即可,最后跑最小费用最大流。

注意费用流的反向边。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 2505
#define M 5005
#define INF 0x3f3f3f3f
struct edge {int v,c,f,w;} edges[M<<1];
int head[N],nxt[M<<1],idx=0;
inline void clear() {idx=0;memset(head,-1,sizeof(head));}
inline void addEdge(int u,int v,int c,int w) {
edges[idx]=(edge){v,c,0,w};nxt[idx]=head[u];head[u]=idx++;
edges[idx]=(edge){u,0,0,-w};nxt[idx]=head[v];head[v]=idx++;
}
int d[N],inq[N],a[N],pa[N];
inline bool SPFA(int s,int t,int &cost) {
memset(d,0x3f,sizeof(d));d[s]=0;inq[s]=true;
std::queue<int> q;q.push(s);a[s]=INF;pa[s]=0;
while(q.size()) {
int x=q.front();q.pop();inq[x]=false;
for(register int i=head[x];~i;i=nxt[i]) {
edge &e=edges[i];
if(e.c>e.f&&d[e.v]>d[x]+e.w) {
d[e.v]=d[x]+e.w;pa[e.v]=i;
a[e.v]=std::min(a[x],e.c-e.f);
if(!inq[e.v]) {q.push(e.v);inq[e.v]=true;}
}
}
}
if(d[t]==INF) return false;
int u=t;cost+=a[t]*d[t];
while(true) {
edges[pa[u]].f+=a[t];
edges[pa[u]^1].f-=a[t];
if(u==s) break;u=edges[pa[u]^1].v;
}
return true;
}
inline int solve(int s,int t) {
int ans=0;
while(SPFA(s,t,ans));
return -ans;
}
int n,p,q;
void dfs(int x,int rt) {
for(register int i=head[x];~i;i=nxt[i]) {
edge &e=edges[i];
if(e.f>0) {
if(e.v<x) printf("%d %d\n",rt,(e.v==x-q*p+1)?1:0);
--e.f;dfs(e.v,rt);break;
}
}
}
int main() {
scanf("%d%d%d",&n,&p,&q);clear();
for(register int i=1;i<=q;++i) {
for(register int j=1;j<=p;++j) {
int state;scanf("%d",&state);
if(state==0) {
addEdge(1+(i-1)*p+j,1+q*p+(i-1)*p+j,INF,0);
}
else if(state==2) {
addEdge(1+(i-1)*p+j,1+q*p+(i-1)*p+j,1,-1);
addEdge(1+(i-1)*p+j,1+q*p+(i-1)*p+j,INF,0);
}
if(i!=q) addEdge(1+q*p+(i-1)*p+j,1+i*p+j,INF,0);
if(j!=p) addEdge(1+q*p+(i-1)*p+j,1+(i-1)*p+j+1,INF,0);
}
}
addEdge(1,2,n,0);addEdge(1+(p*q*2),1+(p*q*2)+1,n,0);
solve(1,1+(q*p*2)+1);for(register int i=1;i<=n;++i) dfs(1,i);
return 0;
}