危险的迷宫
【 问题描述】
近来发现了一个古老的地下迷宫,已探明该迷宫是一个 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
13
1 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
第一步:每一格分为两个顶点。为简便起见,不妨将其中一个顶点称为该格“入点”,
另一顶点称为“出点”。
连一条边从“入点”指向“出点”,其流量为 1,该格的危险程度即为这条边的费用。
第二步:源点指向所有的入口格的入点,其流量均为 1,费用为 0。
所有的出口格的出点指向汇点,其流量均为 1,费用为 0。
第三步:如果两格 A、B 相通,则如下构造:
从单元格 A 的“出点”指向单元格 B 的“入点”连一条边,流量为 1,费用为 0;
从单元格 B 的“出点”指向单元格 A 的“入点”连一条边,流量为 1,费用为 0。
我们不难发现,当到达汇点的总流量与队员数,即入口(出口)数相等时,有解;否则无解。
在有解的情况下,网络流的费用实际上就是所有队员经过的格子的危险程度之和,
“最小费用”其实就是使这个总和最小。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 struct Node 8 { 9 int next,to,u,dis,c; 10 } edge[1000001]; 11 int head[1001],num,n,m,k,path[1001],dist[1001],inf,ans,flow,a[101][101],q; 12 bool vis[1001]; 13 void add(int u,int v,int dis,int c) 14 { 15 edge[num].next=head[u]; 16 edge[num].u=u; 17 edge[num].dis=dis; 18 edge[num].c=c; 19 edge[num].to=v; 20 head[u]=num++; 21 } 22 bool SPFA(int S,int T) 23 { 24 int i; 25 queue<int>Q; 26 memset(path,-1,sizeof(path)); 27 memset(dist,127/2,sizeof(dist)); 28 Q.push(S); 29 inf=dist[0]; 30 dist[S]=0; 31 memset(vis,0,sizeof(vis)); 32 while (Q.empty()==0) 33 { 34 int u=Q.front(); 35 Q.pop(); 36 vis[u]=0; 37 for (i=head[u]; i!=-1; i=edge[i].next) 38 if(edge[i].c>0) 39 { 40 int v=edge[i].to; 41 if (dist[v]>dist[u]+edge[i].dis) 42 { 43 dist[v]=dist[u]+edge[i].dis; 44 path[v]=i; 45 if (vis[v]==0) 46 { 47 vis[v]=1; 48 Q.push(v); 49 } 50 } 51 } 52 } 53 if (dist[T]==inf) return 0; 54 return 1; 55 } 56 int mincost(int S,int T) 57 { 58 int i; 59 while (SPFA(S,T)) 60 { 61 int minf=inf; 62 for (i=path[T]; i!=-1; i=path[edge[i].u]) 63 { 64 minf=min(minf,edge[i].c); 65 } 66 for (i=path[T]; i!=-1; i=path[edge[i].u]) 67 { 68 edge[i].c-=minf; 69 edge[i^1].c+=minf; 70 } 71 ans+=dist[T]; 72 flow+=1; 73 } 74 if (flow<q) return -1; 75 return ans; 76 } 77 int main() 78 { 79 int i,j,x1,x2,y1,y2,x,y,S,T; 80 cin>>n>>m; 81 memset(head,-1,sizeof(head)); 82 for (i=1; i<=n; i++) 83 { 84 for (j=1; j<=m; j++) 85 { 86 scanf("%d",&a[i][j]); 87 add(i*m-m+j,i*m-m+j+n*m,a[i][j],1); 88 add(i*m-m+j+n*m,i*m-m+j,-a[i][j],0); 89 } 90 } 91 cin>>k; 92 for (i=1; i<=k; i++) 93 { 94 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 95 add(x1*m-m+y1+n*m,x2*m-m+y2,0,1); 96 add(x2*m-m+y2,x1*m-m+y1+n*m,0,0); 97 add(x2*m-m+y2+n*m,x1*m-m+y1,0,1); 98 add(x1*m-m+y1,x2*m-m+y2+n*m,0,0); 99 } 100 cin>>q; 101 for (i=1; i<=q; i++) 102 { 103 scanf("%d%d",&x,&y); 104 add(0,x*m-m+y,0,1); 105 add(x*m-m+y,0,0,0); 106 } 107 for (i=1; i<=q; i++) 108 { 109 scanf("%d%d",&x,&y); 110 add(x*m-m+y+n*m,2*n*m+1,0,1); 111 add(2*n*m+1,x*m-m+y+n*m,0,0); 112 } 113 S=0; 114 T=2*n*m+1; 115 printf("%d\n",mincost(S,T)); 116 }