【不知出处】危险的迷宫 网络流 费用流

题目描述

  近来发现了一个古老的地下迷宫,已探明该迷宫是一个A行B列的矩阵,该迷宫有N个不同的出口与N个不同的入口,任一单元格不会既为入口又为出口。为了进一步探明与发掘该迷宫,N个考古队员分别从地上的N个不同的入口进入迷宫,并且计划从N个不同的出口出来。每个队员任意选择一个出口出来,但任意两名队员不会选择同一个出口。
  迷宫中的每一格与其相邻的某些格相通。该迷宫设计非常精妙,在不知道具体机关的情况下,人一旦离开其所在格后,该格将迅速关闭,且再也不能开启,也就是说每一格仅能进入一次。更糟的是,迷宫中的每一格都有一定的危险性,专家们用1至100的整数表示,数值越大表示越危险。正因为如此,再加之每一格都不很宽敞,两人一起进入比较危险,所以规定不能两个人同时进入同一格。
  为了队员们的安全着想,希望你能够编程求出如何使队员们所经过单元格的危险性总和最小。
【样例解释】
  有如下迷宫:
  每一格中的数字表示该格的危险程度。两格间若有空缺,表示这两格相通。
  入口有两个:(1,1)即第一行第一列,(1,2)即第一行第二列
  出口也有两个:(2,3)即第二行第三列,(3,4)即第三行第四列
  两名队员的最好的行动方案之一,如上图红蓝箭头所示。危险程度之和最小为235。

数据范围

A,B<=10

样例输入

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

解题思路

这道题需要保证每个格子只经过一次,所以最好要拆点反正我没拆点WA了
对于A点与B点连通,则A的出点连向B的入点,B的出点连向A的入点,权值1,费用0
每个点的入点连向出点,权值1,费用为危险值
源点向入口连边,权值1,费用0
出口向汇点连边,权值1,费用0
最小费用流
get233

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#define Maxn 233333
#define Maxe 233333
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int A,B,S=0,T,vl[105];
int n,m,k,cnt=0,h[Maxn],dis[Maxn],pre[Maxn],preto[Maxn],vis[Maxn];
struct node{int to,next,v,k,pair;}e[Maxe];
void AddEdge(int x,int y,int v,int kk,int pair){e[cnt]=(node){y,h[x],v,kk,pair};h[x]=cnt;}
void AddEdge(int x,int y,int v,int kk){AddEdge(x,y,v,kk,++cnt+1);AddEdge(y,x,0,-kk,++cnt-1);}
bool SPFA(){
    queue<int>Q;
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,0,sizeof(pre));
    memset(preto,0,sizeof(preto));
    dis[S]=0;
    vis[S]=1;
    Q.push(S);
    while(Q.size()){
        int x=Q.front();
        Q.pop();
        vis[x]=false;
        for(int p=h[x];p;p=e[p].next){
            int y=e[p].to;
            if(e[p].v&&dis[y]>dis[x]+e[p].k){
                pre[y]=p;
                preto[y]=x;
                dis[y]=dis[x]+e[p].k;
                if(!vis[y])Q.push(y);
            }
        }
    }
    return dis[T]<=(1<<29);
}
int Adjust(){
    int flow=1<<30;
    for(int p=T;p!=S;p=preto[p])
        flow=min(flow,e[pre[p]].v);
    for(int p=T;p!=S;p=preto[p]){
        e[pre[p]].v-=flow;
        e[e[pre[p]].pair].v+=flow;
    }
    return dis[T]*flow;
}
int Solve(){
    int Ans=0,tot=0;
    while(SPFA())Ans+=Adjust(),tot++;
    return Ans*(tot==n);
}
void Init(){
    A=Getint();
    B=Getint();
    for(int i=1;i<=A;i++)
        for(int j=1;j<=B;j++)
            vl[(i-1)*B+j]=Getint();
    k=Getint();
    S=0,T=2*A*B+1;
    for(int i=1;i<=A*B;i++){
        AddEdge(2*i-1,2*i,1,vl[i]);
    }
    for(int i=1;i<=k;i++){
        int x1=Getint(),y1=Getint(),p1=(x1-1)*B+y1;
        int x2=Getint(),y2=Getint(),p2=(x2-1)*B+y2;
        AddEdge(p1*2,p2*2-1,1,0);
        AddEdge(p2*2,p1*2-1,1,0);
    }
    n=Getint();
    for(int i=1;i<=n;i++){
        int x=Getint(),y=Getint(),p=(x-1)*B+y;
        AddEdge(S,p*2-1,1,0);
    }
    for(int i=1;i<=n;i++){
        int x=Getint(),y=Getint(),p=(x-1)*B+y;
        AddEdge(2*p,T,1,0);
    }
}
int main(){
    Init();
    int Ans;
    cout<<((Ans=Solve())?Ans:-1);
    return 0;
}

转载于:https://www.cnblogs.com/Cedric341561/p/6811047.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值