题目链接:点击查看

题目大意:给出一个n*n的矩阵表示道路,途中有一些加油站,现在要从点(1,1)到达点(n,n),问最小花费,其中的一些规则如下:

  1. 汽车只能沿着网格边行驶,装满油后可以行驶K条边,出发时已经装满油
  2. 汽车经过一条网格边时,若x或y减小,需要花费B元,其余情况没有花费
  3. 汽车在行驶过程中遇到油库则必须强制加满油,并花费A元
  4. 在需要时可以在网格点增设临时油库,并支付花费C元(不包括加油的A元)
  5. N,K,A,B,C均为正整数,且满足2<=N<=100,2<=K<=10

输出从起点到达终点的最小费用

题目分析:又是条件冗杂的一道题目,不过是一个比较明显的分层图问题,又是要求最小花费,我们可以直接涉及一个动态规划的转移方程,然后用spfa迭代更新就好了,这个一会直接挂个代码就行了,因为最短路写起来比较简单也比较好理解

然后重点说一下怎么用费用流求解吧,因为这个题目是要求最小花费,所以建好图后直接跑费用流也是可行的,关于建图,我们也可以直接建分层图,首先抛去油箱与加油站的情况,如果只是要求从起点到终点的最短路,那么我们直接在一层中进行连边,源点连向起点,流量为1,花费为0,然后按照上面的条件2连边,最后让终点连向汇点就好了,至于多出来的油箱限制,我们可以将每一种情况视为新的一层,我选择的是将k拆为k层图,每一层图代表当前油箱剩余的油还有多少,那么在转移的时候,符合条件的连边就可以直接从(x,y,k)连边到(xx,yy,k-1)了,对于每个油库,因为是强制满油,所以我们可以将该点[0,k-1]层的点都向第k层的点连边,花费为a,只有第k层才能向四周连边,而对于非油库的点,最优解肯定是当油箱空了的时候才建立临时油库,所以只需要让第0层的该点向第k层建边,花费为a+c就好了,具体细节还真就没有了,主要还是在代码实现的过程中一定要细心细心再细心,一开始建边的时候因为小于等于漏了一个等于号,调了20多分钟。。太菜了:

三维点表示的是(x,y,k),x,y代表坐标,k代表层数

  1. 源点->(1,1,k),流量为1,花费为0
  2. (x,y,k)->(xx,yy,k-1),流量为无穷大,花费符合条件2
  3. 当前点是否为油库:
    1. 是油库:(x,y,t)t∈[0,k-1]->(x,y,k),流量为无穷大,花费为a
    2. 不是油库:(x,y,0)->(x,y,k),流量为无穷大,花费为a+c
  4. (n,n,t)t∈[0,k]->汇点,流量为无穷大,花费为0

关于费用流的建图就到此为止了,按照上述要求建好图后直接跑最小费用最大流就是答案了

代码:

最小费用最大流:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e5+100;//点const int M=1e6+100;//边const int bb[4][2]={0,1,0,-1,1,0,-1,0};struct Edge
{int to,w,cost,next;
}edge[M];int head[N],cnt;void addedge(int u,int v,int w,int cost)
{edge[cnt].to=v;edge[cnt].w=w;edge[cnt].cost=cost;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].to=u;edge[cnt].w=0;edge[cnt].cost=-cost;edge[cnt].next=head[v];head[v]=cnt++;
}int d[N],incf[N],pre[N],n;bool vis[N];bool spfa(int s,int t)
{memset(d,inf,sizeof(d));memset(vis,false,sizeof(vis));memset(pre,-1,sizeof(pre));queue<int>q;q.push(s);vis[s]=true;incf[s]=inf;d[s]=0;while(!q.empty()){int u=q.front();q.pop();vis[u]=false;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;int cost=edge[i].cost;if(!w)continue;if(d[v]>d[u]+cost){d[v]=d[u]+cost;pre[v]=i;incf[v]=min(incf[u],w);if(!vis[v]){vis[v]=true;q.push(v);}}}}return pre[t]!=-1;
}int update(int s,int t)
{int x=t;while(x!=s){int i=pre[x];edge[i].w-=incf[t];edge[i^1].w+=incf[t];x=edge[i^1].to;}return d[t]*incf[t];
}void init()
{memset(head,-1,sizeof(head));cnt=0;
}int solve(int st,int ed)
{int ans=0;while(spfa(st,ed))ans+=update(st,ed);return ans;
}int get_id(int x,int y,int k)//第k层的(x,y)
{return (x-1)*n+y+k*n*n;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);init();int k,a,b,c,st=N-1,ed=st-1;scanf("%d%d%d%d%d",&n,&k,&a,&b,&c);addedge(st,get_id(1,1,k),1,0);//源点->(1,1,k) for(int i=0;i<=k;i++)//(n,n,kk)kk∈[0,k]->汇点 addedge(get_id(n,n,i),ed,inf,0);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){int val;scanf("%d",&val);if(val)//强制加油{for(int t=0;t<k;t++)//(i,j,kk)kk∈[0,k-1]->(i,j,k) addedge(get_id(i,j,t),get_id(i,j,k),inf,a);for(int t=0;t<4;t++)//(i,j,k)->(xx,yy,k-1) {int xx=i+bb[t][0];int yy=j+bb[t][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<i||yy<j)len=b;addedge(get_id(i,j,k),get_id(xx,yy,k-1),inf,len);}} else{for(int t=0;t<4;t++){int xx=i+bb[t][0];int yy=j+bb[t][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<i||yy<j)len=b;for(int kk=1;kk<=k;kk++)addedge(get_id(i,j,kk),get_id(xx,yy,kk-1),inf,len);}addedge(get_id(i,j,0),get_id(i,j,k),inf,a+c);}}printf("%d\n",solve(st,ed));return 0;
}

分层图最短路(spfa):

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=110;const int bb[4][2]={0,1,0,-1,1,0,-1,0};int maze[N][N],d[N][N][15],n,K,a,b,c;bool vis[N][N][15];struct Node
{int x,y,k;Node(int X,int Y,int K){x=X;y=Y;k=K;}
};void spfa()
{memset(vis,false,sizeof(vis));memset(d,inf,sizeof(d));queue<Node>q;q.push(Node(1,1,K));vis[1][1][K]=true;d[1][1][K]=0;while(q.size()){Node cur=q.front();q.pop();int x=cur.x;int y=cur.y;int k=cur.k;vis[x][y][k]=false;if(k<K)//加油{if(maze[x][y])//有加油站{if(d[x][y][K]>d[x][y][k]+a){d[x][y][K]=d[x][y][k]+a;if(!vis[x][y][K]){vis[x][y][K]=true;q.push(Node(x,y,K));}}continue;//强制加油}else//没加油站 {if(d[x][y][K]>d[x][y][k]+a+c){d[x][y][K]=d[x][y][k]+a+c;if(!vis[x][y][K]){vis[x][y][K]=true;q.push(Node(x,y,K));}}} } if(k)//跑路{for(int i=0;i<4;i++){int xx=x+bb[i][0];int yy=y+bb[i][1];if(xx<=0||yy<=0||xx>n||yy>n)continue;int len=0;if(xx<x||yy<y)len=b;if(d[xx][yy][k-1]>d[x][y][k]+len){d[xx][yy][k-1]=d[x][y][k]+len;if(!vis[xx][yy][k-1]){vis[xx][yy][k-1]=true;q.push(Node(xx,yy,k-1));}}}} }
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);scanf("%d%d%d%d%d",&n,&K,&a,&b,&c);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&maze[i][j]);spfa();int ans=inf;for(int i=0;i<=K;i++)ans=min(ans,d[n][n][i]);printf("%d\n",ans);return 0;
}

洛谷 - P4009 汽车加油行驶问题(分层图最短路/最小费用最大流)相关推荐

  1. 洛谷 P4009 汽车加油行驶问题 题解

    原题题面 网络流24题?费用流?不存在的 tag都是骗人的 首先看题,仔细想想,这不就是一个最短路吗? 然而本蒟蒻看不出一些题解中说的分层图最短路,于是直接写单源最短路 从 (1,1) 往 (n,n) ...

  2. 【线性规划与网络流24题】汽车加油行驶问题 分层图

    汽车加油行驶问题 Time Limit: 1 Sec  Memory Limit: 128 MB Description 给定一个 N*N的方形网格,设其左上角为起点◎,坐标为( 1,1),X轴向右为 ...

  3. Loj#6223 Luogu P4009 汽车加油行驶 分层图最短路

    这是本蒟蒻博客的第一篇文章,不规范之处敬请各位大佬指正和谅解orz Loj#6223+Luogu P4009 文章目录 前言 一.建模 二.代码实现 1.节点在图中的编号(分层图的存储) 2.建边 对 ...

  4. P4009 汽车加油行驶问题

    题目描述: 题解: 看了很多题解,无论什么解法都绕不开分层图 在本题中加满油的车每次可以移动K步,那么我们就可以建立一个K+1层的分层图,表示汽车油量k的状态(油量0-k),然后根据题目要求建图 首先 ...

  5. 洛谷4400 BlueMary的旅行(分层图+最大流)

    qwq 首先,我们观察到题目中提到的每天只能乘坐一次航班的限制,很容易想到建分层图,也就是通过枚举天数,然后每天加入一层新的点. (然而我一开始想的却是erf) 考虑从小到大枚举天数,然后每次新建一层 ...

  6. 洛谷P1073 Tarjan + 拓扑排序 // 构造分层图

    https://www.luogu.org/problemnew/show/P1073 C国有 n n个大城市和 mm 条道路,每条道路连接这 nn个城市中的某两个城市.任意两个城市之间最多只有一条道 ...

  7. 洛谷 - P3357 最长k可重线段集问题(最大费用最大流+思维建边+拆点)

    题目链接:点击查看 题目大意:给出n条开线段,开线段的意思就是端点的两个点属于开区间,不属于线段中,让从中选出数条线段,满足: 在x轴选取任何一个点,选取线段向x轴映射到该点的次数小于等于k 所选线段 ...

  8. 洛谷 - P3358 最长k可重区间集问题(最大费用最大流+思维建边)

    题目链接:点击查看 题目大意:给出n个开区间,现在要求从中选取一定数量的区间,需要满足: 对于任意点x,所选取的区间中包含点x的个数小于等于k 区间长度和最大 要求输出最长的区间长度和 题目分析:一开 ...

  9. 孤岛营救与汽车加油行驶问题

    题目链接:https://www.luogu.org/problemnew/show/P4011 (孤岛营救)|| https://www.luogu.org/problemnew/show/P400 ...

最新文章

  1. python数据结构与算法:排序算法(面试经验总结)
  2. 关于SSIS中解密数据库字符串的方法
  3. 【STM32】按键检测实验主要程序代码分析
  4. 深度学习——05、深度学习框架Caffe
  5. SqlBulkCopy 批量复制数据到数据表
  6. 非华为手机可以用鸿蒙吗,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  7. 项目落地才是硬道理!TensorFlow 2 牛了
  8. svn ignore 的用法
  9. STM8学习笔记---定时器 TIM2功能实现
  10. kafka java 生产消费程序demo示例
  11. 选择数据分析软件时要注意什么
  12. Gitlab的管理使用手册
  13. CF959A Mahmoud and Ehab and the even-odd game
  14. keli下使用断点调试
  15. 仓储管理之计价方法——月末一次加权平均法
  16. 谷歌应用程序无法启动,因为应用程序的并行配置不正确的问题解决方案
  17. html表格列文字自动换行,Table文字自动换行
  18. 泛函分析 01.03 距离空间-开集和连续映射
  19. C. Minimum Ties (构造)
  20. Unity应该怎样学习

热门文章

  1. Pod详解-生命周期-概述
  2. 一致性hash算法原理
  3. Curator实现分布式锁的基本原理-LockInternals.attemptLock
  4. 为什么要用MyBatis-Spring JDBC
  5. rocketmq控制台安装
  6. SpringBoot另一大神器-Actuator
  7. 引导类、扩展类、系统类加载器的使用及演示
  8. 枚举类 - Java面向对象编程
  9. Spring Schedule定时关单快速入门
  10. 设计模式之_Iterator_04