• 危险的迷宫


    【 问题描述】
    近来发现了一个古老的地下迷宫,已探明该迷宫是一个 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
    ",mincost(S,T));
    116 }
  • 相关阅读:
    程序员如何利用空闲时间挣零花钱
    常见的数据交互之跳转页面传值
    一个能让cin和scanf 一样快的方法:
    HDU 4901 DP
    POJ 2823 线段树 Or 单调队列
    POJ 3264 线段树 ST
    POJ 3468 线段树+状压
    POJ 2777 线段树
    QQ 临时会话+图标 HTML代码
    POJ 1463 Strategic game
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7729812.html
Copyright © 2020-2023  润新知