这是本蒟蒻博客的第一篇文章,不规范之处敬请各位大佬指正和谅解orz

Loj#6223+Luogu P4009

文章目录

前言

一、建模

二、代码实现

1.节点在图中的编号(分层图的存储)

2.建边

对于非油库

对于油库

3.求解

三、AC代码

总结


前言

本蒟蒻写博客发题解主要是为了加深对题目的理解,并且在枯燥的刷题过程中换换脑子理清思路,毕竟该蒟蒻博客很可能只有我一个人看(或者两个?),不管怎么说,一定会认真对待的。

Pass:声明:本博客参考:

P4009 汽车加油行驶问题(分层图最短路)_Mr.Gzj的博客-CSDN博客


一、建模

这道题其实代码和实现难度都很低,关键在于如何建模。

显然看到网络流24题基本上各方神犇都去跑最小费用最大流了,但蒟蒻这里提供另外一个方法——

分层图最短路

既然是求最小边权,我们容易想到是以费用为边权建图。

那么问题来了,如何建图呢?

网格图,那么最基本的就是根据题意描述,向四个放向连边。

但这道题真正难在对汽油的处理。

我们发现,汽油这一状态较为复杂.。

这里就需要引入分层图了。

把图复制若干张,对于每张图表示不同的状态,根据题意对不同的图层进行连边,这就是分层图。

题目中油箱容量为K

——实际上根据题目描述是满油可以走K条网格边,

这里曲解为油箱容量为K,每走一条边消耗一单位的油,方便理解——

所以我们需要建K+1张图,表示油箱含有0-K个单位的油

(也可以表示用了0-K单位的油,实现时微调即可)

如果要加满油,就向第K层(即油量为K的层)连边,

对于每一步,向下一层(假设是当前第Lay层,则下一层为Lay-1层(下同))连边

二、代码实现

1.节点在图中的编号(分层图的存储)

我们不妨把编号理解为一个点在图中的次序。

对于每一张网格图,行编号小的次序靠前,在同一行,列编号小的次序靠前。

那么对于K+1张图,依次排列即可。

节点的编号边是在它前面(次序比它小的)的节点的个数+1。

假设点p在第k张图的(i,j),那么它前面的节点数有:

前面图层的节点数n*n*k(存在第0层所以k不用减一)

前面行的节点数n*(i-1)

所在行前面的节点数(j-1)

加起来再加一即可

求编号代码如下:

int Get_Node(int x,int y,int z)//第z层的(x,y)的编号
{return (n*n*z)+(n*(x-1))+y;  //下层节点数+该行前节点数+列编号
}

2.建边

我们需要对油库和非油库分开考虑

对于非油库

汽车经过一条网格边时,若其 X坐标或Y坐标减小,则应付费用B,否则免付费用

那么建边方式就很显然了,向下一层的左和上方节点连一条权值为B的边。

向下一层的右和下方节点连一条权值为0的边。

在需要时可在网格点处增设油库,并付增设油库费用 C(不含加油费用A)

这也很显然,我们向第K层连一条权值为A+C的边。

一个小心思,读题不认真可能漏掉,不含加油费说明建厂的话还得交加油钱,要再加一个A。

对于油库

汽车在行驶过程中油库则应加满油并付加油费用 A

注意文字游戏,遇..则应,意味着强制消费,也无形埋下了一个坑。

既然是强制消费,那我们就有不同的建边方法。

首先肯定是向第K层建一条权值为A的边。

但既然是强制消费,那我们是不是就可以理解为不加满不给走?

那么不就是加满了才能走吗(废话文学)

所以我们不妨只由第K层向第K-1层的四个方向建相应的边,就可以实现只有满油才能离开了!

加边代码如下:

for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){int Hasoil=Read();if(Hasoil)//油库 {for(int Lay=0;Lay<k;Lay++)//加油(理论上不会出现加油之前满油的情况,Lay<k)Addedge(Get_Node(i,j,Lay),Get_Node(i,j,k),a);if(i<n) Addedge(Get_Node(i,j,k),Get_Node(i+1,j,k-1),0);if(j<n)Addedge(Get_Node(i,j,k),Get_Node(i,j+1,k-1),0);//往前跑免费 if(i>1)Addedge(Get_Node(i,j,k),Get_Node(i-1,j,k-1),b);if(j>1)Addedge(Get_Node(i,j,k),Get_Node(i,j-1,k-1),b);//往回跑 交费 //强制消费只连满和满减一 }else//非油库 {for(int Lay=0;Lay<k;Lay++)//建厂+加油 Addedge(Get_Node(i,j,Lay),Get_Node(i,j,k),a+c);for(int Lay=1;Lay<=k;Lay++)//注意第0层没油跑不动 {if(i<n)Addedge(Get_Node(i,j,Lay),Get_Node(i+1,j,Lay-1),0);if(j<n)Addedge(Get_Node(i,j,Lay),Get_Node(i,j+1,Lay-1),0);if(i>1)Addedge(Get_Node(i,j,Lay),Get_Node(i-1,j,Lay-1),b);if(j>1)Addedge(Get_Node(i,j,Lay),Get_Node(i,j-1,Lay-1),b);}}}

3.求解

图建好了剩下就简单多了。

起点为左上角,终点为右下角,且:

出发时汽车已装满油,在起点与终点处不设油库。

那么我们以第K层的(1,1)为起点,对整个图跑一遍SPFA/Dijstra,

(没负权网格其实用堆优化的Dijstra更好, 但蒟蒻SPFA写得熟,但数据不卡无所谓了)

最后对于每层图的(n,n),求最小值,就可以华丽地输出了!

三、AC代码

Words are useless,I need your code....

#include<bits/stdc++.h>
const int N=1e6+100;
const int INF=0x3f3f3f3f;
//数组其实开大了, 点开到1e5,边开到1e6就好了
using namespace std;
int n,k,a,b,c;
inline int Read()
{int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;
}
int Head[N],To[N<<1],Nxt[N<<1],W[N<<1],Etot;
void Addedge(int x,int y,int z)//加边
{Nxt[++Etot]=Head[x];W[Etot]=z;To[Etot]=y;Head[x]=Etot;
}
int Get_Node(int x,int y,int z)//第z层的(x,y)的编号
{return (n*n*z)+(n*(x-1))+y;  //下层节点数+该行前节点数+列编号
}
void Init()
{n=Read(),k=Read(),a=Read(),b=Read(),c=Read();for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){int Hasoil=Read();if(Hasoil)//油库 {for(int Lay=0;Lay<k;Lay++)//加油(理论上不会出现加油之前满油的情况) Addedge(Get_Node(i,j,Lay),Get_Node(i,j,k),a);if(i<n) Addedge(Get_Node(i,j,k),Get_Node(i+1,j,k-1),0);if(j<n)Addedge(Get_Node(i,j,k),Get_Node(i,j+1,k-1),0);//往前跑免费 if(i>1)Addedge(Get_Node(i,j,k),Get_Node(i-1,j,k-1),b);if(j>1)Addedge(Get_Node(i,j,k),Get_Node(i,j-1,k-1),b);//往回跑 交费 //强制消费只连满和满减一 }else//非油库 {for(int Lay=0;Lay<k;Lay++)//建厂+加油 Addedge(Get_Node(i,j,Lay),Get_Node(i,j,k),a+c);for(int Lay=1;Lay<=k;Lay++)//注意第0层没油跑不动 {if(i<n)Addedge(Get_Node(i,j,Lay),Get_Node(i+1,j,Lay-1),0);if(j<n)Addedge(Get_Node(i,j,Lay),Get_Node(i,j+1,Lay-1),0);if(i>1)Addedge(Get_Node(i,j,Lay),Get_Node(i-1,j,Lay-1),b);if(j>1)Addedge(Get_Node(i,j,Lay),Get_Node(i,j-1,Lay-1),b);}}}
}
queue<int>Q;
int Dis[N];
bool Inque[N];
void SPFA(int st)
{memset(Dis,0x3f,sizeof(Dis));Q.push(st);Inque[st]=true;Dis[st]=0;while(!Q.empty()){int u=Q.front();Q.pop();Inque[u]=false;for(int i=Head[u];i;i=Nxt[i]){int v=To[i];if(Dis[u]+W[i]<Dis[v]){Dis[v]=Dis[u]+W[i];if(!Inque[v]){Q.push(v);Inque[v]=true;}}}}
}
int main()
{Init();SPFA(Get_Node(1,1,k));int ans=INF;for(int i=0;i<=k;i++)ans=min(Dis[Get_Node(n,n,i)],ans);printf("%d\n",ans);return 0;
}
//完结撒花 !!! 

算法期望时间复杂度O(k*N*N*K)


总结

这道题其实算是分层图最短路的一道比较好的模板,其实用最短路来理解的话会比网络流想起来容易点?(主要是蒟蒻网络流太逊)

其实这道题和拯救大兵瑞恩(孤岛营救问题Loj6121+LuoguP4011)异曲同工也是网络流24题,也可以用分层图最短路做。。。。

可以双倍经验一下。

作为本博客第一篇,蒟蒻感谢每一位来访者。

霍格沃茨空中使用的魔法太多了,麻瓜使用的魔法的替代品——像蒸汽和电脑等在这里变得一塌糊涂。

——《霍格沃茨:一段校史》

Loj#6223 Luogu P4009 汽车加油行驶 分层图最短路相关推荐

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

    题目链接:点击查看 题目大意:给出一个n*n的矩阵表示道路,途中有一些加油站,现在要从点(1,1)到达点(n,n),问最小花费,其中的一些规则如下: 汽车只能沿着网格边行驶,装满油后可以行驶K条边,出 ...

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

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

  3. P4009 汽车加油行驶问题

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

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

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

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

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

  6. [codevs 1912] 汽车加油行驶问题

    http://codevs.cn/problem/1912/ 题解: 看到题后第一反应就是费用流,因为求的就是最小费用,还要加入邮箱中剩余油量为参数,因为剩余油量不同到达那个结点后所要执行的操作也不同 ...

  7. 图论 ---- E. Minimum Path(分层图最短路 用分层图对边权操作进行选择)

    题目链接 题目大意: 两点间最短路的定义变成:所有的边之和−max+min所有的边之和-max+min所有的边之和−max+min 解题思路: 这里很明显就是变成了最短路的时时候就是把路径上边权最小值 ...

  8. BZOJ2662[BeiJing wc2012]冻结——分层图最短路

    题目描述 "我要成为魔法少女!"      "那么,以灵魂为代价,你希望得到什么?"  "我要将有关魔法和奇迹的一切,封印于卡片之中„„"  ...

  9. POJ - 3662 Telephone Lines(分层图最短路)

    题目链接:点击查看 题目大意:在郊区有N座通信基站,P条双向电缆,第i条电缆连接基站Ai和Bi.特别的,1号基站是通信公司的总站,N号基站位于一座农场中.现在,农场主希望对通信线路进行升级,其中升级第 ...

最新文章

  1. R语言ggplot2可视化设置数据点的大小、设置数据点的颜色、设置数据点边缘线条的厚度(point border thickness)实战
  2. Android中Preference的使用以及监听事件分析
  3. druid不能close mysql连接_alibaba druid mysql连接问题
  4. 静态方法+工厂方法(静态方法用途)
  5. redis分布式锁java代码_基于redis实现分布式锁
  6. [原]Oracle外部表结合游标完成统计一例
  7. 【疾病分类】基于matlab SVM农作物叶子虫害识别与分类【含Matlab源码 624期】
  8. HR人事管理系统源码
  9. 宗成庆统计自然语言处理第二版第13章读书笔记-文本分类与情感分类
  10. 系统分析师真题__专项:计算机系统与配置 1
  11. 饿了么UI组件库中,Upload组件上传闪动的解决
  12. 金蝶k3系统中间服务器不可用,【金蝶软件】客户端登陆时提示远程服务器不存在或不可用(金蝶K3系统)...
  13. P2342 叠积木 并查集
  14. kivy开发android启动器,从kivy启动android播放器
  15. 章文嵩:开源,LVS,以及留下的无数传说
  16. 什么是词频?词频的原理是什么?
  17. vue设置网页title
  18. Let_god_knows
  19. oracle关于索引
  20. 猫狗大战——基于TensorFlow的猫狗识别(1)

热门文章

  1. c++ opengl 三维图形中显示文字_3D图形 和 OpenGL 简介
  2. Remember Me 功能实现
  3. 微信小程序部分手机预览pdf没反应
  4. 病毒检测(BF实现)
  5. Go语言实现的API-Gateway
  6. android锁屏快捷键设置,Android4.0+锁屏程序开发——设置锁屏页面篇
  7. latex bib是什么 从哪里获取 文献引用
  8. 帝企鹅日记(史上第二受欢迎记录片 老少皆宜的冰雪童话)
  9. dht22温湿度传感器特点及使用介绍
  10. java 实现 公式计算