【 问题描述】
近来发现了一个古老的地下迷宫,已探明该迷宫是一个 A 行 B 列的矩阵,该迷宫有 N
个不同的出口与 N 个不同的入口,任一单元格不会既为入口又为出口。为了进一步探明与
发掘该迷宫,N 个考古队员分别从地上的 N 个不同的入口进入迷宫,并且计划从 N 个不同
的出口出来。每个队员任意选择一个出口出来,但任意两名队员不会选择同一个出口。
迷宫中的每一格与其相邻的某些格相通。该迷宫设计非常精妙,在不知道具体机关的情
况下,人一旦离开其所在格后,该格将迅速关闭,且再也不能开启,也就是说每一格仅能进
入一次。更糟的是,迷宫中的每一格都有一定的危险性,专家们用 1 至 100 的整数表示,数
值越大表示越危险。正因为如此,再加之每一格都不很宽敞,两人一起进入比较危险,所以
规定不能两个人同时进入同一格。
为了队员们的安全着想,希望你能够编程求出如何使队员们所经过单元格的危险性总和
最小。
有如下迷宫:
每一格中的数字表示该格的危险程度。两格间若有空缺,表示这两格相通。
入口有两个:
(1,1)即第一行第一列,(1,2)即第一行第二列
出口也有两个:
(2,3)即第二行第三列,
(3,4)即第三行第四列
两名队员的最好的行动方案之一,如上图红蓝箭头所示。危险程度之和最小为 235。
【输入描述】
第一行是两个整数 A 与 B(1≤A,B≤10),中间用空格分隔,表示该迷宫是 A 行 B 列的。
第 2 行至第 A+1 行,每行有 B 个 1 至 100 以内的整数,表示该迷宫每一格的危险程度。
以下一行是一个整数 K。接着 K 行每行有四个整数 X0,Y0,X1,Y1,(1 ≤X0,X1≤A, 1≤Y0,Y1
≤B) ,表示(X0,Y0),(X1,Y1)为相邻的两格,这两格互相相通。
接着一行是一个整数 N(0≤N≤A*B/2)
,表示有 N 个出口与入口,保证出入口不会重
合。
以下 N 行,每行有两个整数 X0,Y0,表示每个入口的行列位置。
以下还有 N 行,每行有两个整数 X1,Y1,表示每个出口的行列位置。
【输出描述】
输出仅一个数,若队员们不能全部到达指定目标位置,则输出-1;否则输出所有队员所
经过的所有单元格的危险程度之和。
【输入样例】
3 4
20 30 40 30
30 60 20 20
20 15 20 20
131 1 2 1
1 2 1 3
1 2 2 2
1 3 1 4
1 4 2 4
2 1 2 2
2 1 3 1
2 2 2 3
2 3 2 4
2 4 3 4
3 1 3 2
3 2 3 3
3 3 3 4
2
1 1
1 2
2 3
3 4
【输出样例】
235
这其实是一道最小费用最大流的‘模板题’.
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long #define il inline #define db double using namespace std; il int gi() { int x=0,y=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') y=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*y; } int n,m; int src,des; int head[200045],cnt=1; struct edge { int next,to,cap,cost; }e[200045]; il void add(int from,int to,int cap,int cost) { e[++cnt].next=head[from]; e[cnt].to=to; e[cnt].cap=cap; e[cnt].cost=cost; head[from]=cnt; } int map[45][45]; il int get_node(int x,int y) { return (x-1)*m+y; } il void build(int from,int to,int cap,int cost)//将双向边 { add(from,to,cap,cost);//正边 add(to,from,0,-cost);//负边 } int ans; int flow; int t[100045]; int headd,tail; bool vis[100045]; int dist[100045],pre[100045];//pre保存边的编号 il bool bfs() { memset(dist,127/3,sizeof(dist)); headd=0,tail=1; t[0]=src; dist[src]=0; vis[src]=1; while(headd!=tail) { int h=t[headd++]; vis[h]=0; int r=head[h]; while(r!=-1) { int now=e[r].to; if(dist[now]>dist[h]+e[r].cost&&e[r].cap>0) { dist[now]=dist[h]+e[r].cost; pre[now]=r; if(!vis[now]) { vis[now]=1; t[tail++]=now; } } r=e[r].next; } } if(dist[des]!=dist[100000]) return 1; else return 0; } il void back() { int now=des; while(now!=src) { ans+=e[pre[now]].cost; e[pre[now]].cap--; e[pre[now]^1].cap++; now=e[pre[now]^1].to; } } il void min_flow() { while(bfs())//可以跑到终点 { back();//把路上的边都更新 flow++; } } int main() { freopen("maze.in","r",stdin); freopen("maze.out","w",stdout); memset(head,-1,sizeof(head)); n=gi(),m=gi(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { map[i][j]=gi(); build(get_node(i,j),get_node(i,j)+n*m,1,map[i][j]);//拆点建边 } int k=gi(); int x1,y1,x2,y2; for(int i=1;i<=k;i++) { x1=gi(),y1=gi(),x2=gi(),y2=gi(); build(get_node(x1,y1)+n*m,get_node(x2,y2),1,0); build(get_node(x2,y2)+n*m,get_node(x1,y1),1,0); } int q=gi(); int x,y; src=0,des=n*m*2+1; for(int i=1;i<=q;i++) { x=gi(),y=gi(); build(src,get_node(x,y),1,0); } for(int i=1;i<=q;i++) { x=gi(),y=gi(); build(get_node(x,y)+n*m,des,1,0); } min_flow();//跑费用流 if(flow==q) printf("%d ",ans); else printf("-1 "); return 0; }