题目背景

applepi 想进行宇宙旅行。当然,applepi 知道这是有可能的,因为applepi 的特殊能力能使他观测到宇宙中的虫洞。所谓虫洞就是一个在三维之外的维度打开的快捷通道,通过虫洞能够从一个地方瞬间移动到另外一个地方。

题目描述

为了简化问题,我们建立一个一维坐标系,地球的位置为\(0\),而applepi 的目的地的位置是一个正整数W。在每一个单位时间里,applepi可以向正方向移动不超过\(S\) 的一个整数。虫洞可以被表示为二元组\((B, E)\),即如果在某次移动之后applepi 在位置\(B\),那么applepi 就会被立刻传送到位置\(E\)。注意,applepi 在移动过程中如果经过位置\(B\),由于applepi 的速度极快是不会被传送的。而且,applepi 不能够向负方向移动,但是虫洞引起的除外。现在applepi 想请你帮助他计算一下他至少需要多少个单位时间才能够到达目的地。

输入格式

输入包含多组测试数据。

每组测试数据的第一行是三个正整数 \(W,S,P\),表示目的地位置,移动限制和虫洞的数目。之后 \(P\) 行,每行两个整数\(B\) 和\(E\),表示一个虫洞。

输入文件的最后一行是一个整数 \(0\),表示输入的结束。

输出格式

对于每组测试数据,在单独的一行内输出结果。

对于 \(100%\) 的数据,\(W\leq 10^9,2\leq S\leq 6,1\leq p\leq 40\),没有\(B = 0\) 或者\(B = W\) 的虫洞,输入数据保证目的地可达。


简单图论那个题单点进来的,本来想A了以后就去做其它图论题单上的题,结果一天就调出来这一个。。。

首先,我觉得这不能是一个绿题,差不多要是蓝,虽然思路不难,但实现需要点功夫,所以,如果管理员看见希望可以考虑一下这个难度是否恰当当然也有可能是我太蒻


首先离散化:

言归正传,观察到,\(w\)特别大,但\(p,s\)很小,所以可以考虑从\(p,s\)入手开始乱搞

建图肯定不能一个数轴点对应一个节点了,考虑到起较大作用的点只有虫洞的两个端点

所以用一下离散化,只保留虫洞入口和出口,当然还要保留起点\((0)\)和终点\((w)\)这样点数是\(2p+2\),做完离散化后存在\(map\)数组中

关于离散化,通俗的讲,就是把没用的元素去掉,比如这个题两个虫洞的端点之间的那一堆点,就是没用的,不必保存下来,只保存有用的点(虫洞端点,起点终点),存下他们的坐标就行(存坐标的那个\(map\)数组的下标也就是建图时的节点编号)

然后还要排个序,方便下面操作

这玩意也听常用,更为深入的自己去百度吧,真的不难

然后想办法建图:

那么如何建图?

首先虫洞的起点和终点肯定是要连一条边权为\(0\)的边,至于如何通过点的组标确定其对应的节点编号,用二分实现,这是离散化的传统技能

因为我们走路肯定要借助虫洞,不用虫洞走的路就是:从一个虫洞的终点,走到另一个虫洞的起点

因此,我们可以枚举每两个有用的点,计算他们的的距离,如果第一个点的坐标是一个起点,就不计算直接返回无穷大

因为我们不能从一个起点开始走,走到那会接着被传送走,但是我们可以走到一个起点,走到那以后接着会从边长为\(0\)的虫洞边走到它对应的终点

对于如何判断一个点是否是起点,我用的map<int,int>,对每个起点打上标记,题解区好多大佬用到是set,不过本质一样

再考虑非虫洞边的边权咋计算:

设\(x,y\)分别是要计算的这条边的起点,终点

首先想到的是\(\lceil\dfrac{y-x}{s}\rceil\)

然而这样写完以后就会像我一样得到10分的好成绩

为什么?因为在\([x,y]\)中,还有一些点是虫洞入口,是不能踩的,一踩就被传送走了

例子楼上有大佬给出了

那我们可以考虑递归的计算(其实一开始想写成非递归,炸了,看来题解就写的递归

先贪心的每步都走\(s\),看看这样走第一个遇到的虫洞入口是哪个

至于如何实现,肯定就是枚举每个虫洞,都说了p很小

遇到了虫洞入口,就要一格一格往回退,直到遇见了不是虫洞入口的格子

那如果我又退回来起点,说明怎么走都白搭,走不了返回无穷大

没有退回到起点,假设退到\(y'\),那么从\(x\)到\(y'\)肯定就是\(\lceil\dfrac{y'-x}{s}\rceil\)了

那么\(y'\rightarrow y\)的费用,就可以递归计算了,递归到\(x=y\),费用肯定为\(0\),就返回

还有一个细节就是,当遇到了虫洞起点要开始往回退的时候,要判断一下当前这个入口是不是就是\(y\),如果是,那么说明已经走到了地方,就不退了

最后跑最短路

随便用什么算法跑个最短路就行,范围很小

如果你用dfs就当我没说

最后放上代码,可以从注释中体会一下我调代码时的心情

#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
#define reg register
#define EN std::puts("")
inline int read(){
int x=0,y=1;
char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=-1;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return x*y;
}
int n,p,s;
int map[106],tmp[106],tot;
std::map<int,int>start;
int dis[105][105];
int from[105],to[105];
inline int isstart(int x){//判断pos是不是一个起点
return start.find(x)==start.end()?0:1;
}
inline int find(int pos){//这个函数二分查找pos这个点对应的数组下标
reg int l=1,r=tot,mid,ret;
while(l<=r){
mid=(l+r)>>1;
if(map[mid]>=pos) ret=mid,r=mid-1;
else l=mid+1;
}
return ret;
}
inline int getdis(int x,int y){
if(x==y) return 0;
if(isstart(x)) return 0x3f3f3f3f;//x是个起点,不行
int yy=y;
for(reg int i=1;i<=p;i++)if(from[i]>x){
if(yy>from[i]&&(from[i]-x)%s==0) yy=from[i];
}
while(yy!=y&&isstart(yy)) yy--;//如果yy已经等于y了,就算是一个起点也不再退了
if(yy==x) return 0x3f3f3f3f;//退回了起点,永远到不了了qwq
return getdis(yy,y)+std::ceil((double)(yy-x)/s);
}
int main(){for(;;){
n=read();
if(!n) return 0;
s=read();p=read();
tot=0;start.clear();
tmp[++tot]=0;tmp[++tot]=n;
for(reg int i=1;i<=p;i++){
tmp[++tot]=from[i]=read();tmp[++tot]=to[i]=read();
start[from[i]]=1;
// std::printf("%d %d\n",start.find(from[i]),start.end());
}
//开始排序和去重
std::sort(tmp+1,tmp+1+tot);
reg int hq=tot;tot=0;
map[++tot]=tmp[1];
for(reg int i=2;i<=hq;i++)if(tmp[i]!=tmp[i-1])
map[++tot]=tmp[i];
//下面开始建边
std::memset(dis,0x3f,sizeof dis);
for(reg int i=1;i<=p;i++) dis[find(from[i])][find(to[i])]=0;
for(reg int i=1;i<=tot;i++)
for(reg int j=i+1;j<=tot;j++)
dis[i][j]=std::min(dis[i][j],getdis(map[i],map[j]));
//floyd
for(reg int k=1;k<=tot;k++)
for(reg int i=1;i<=tot;i++)
for(reg int j=1;j<=tot;j++) dis[i][j]=std::min(dis[i][j],dis[i][k]+dis[k][j]);
std::printf("%d\n",dis[1][tot]); // for(reg int i=1;i<=tot;i++) std::printf("%d ",map[i]);std::puts("");
// for(reg int i=1;i<=p;i++) std::printf("%d %d\n",find(from[i]),find(to[i]));std::puts("");
// for(reg int i=1;i<=tot;i++){
// std::printf("%d: ",i);
// for(reg int j=1;j<=tot;j++) std::printf("%d ",dis[i][j]);
// std::puts("");
// }
}
}

P2620 虫洞的更多相关文章

  1. 题解 P2620 虫洞

    总体思路:离散化 + 建图 + 单源最短路(看见人少蒟蒻才敢发题解QAQ) 需要注意的是: 考虑到w范围较大,而实际虫洞数量较小,就只记录虫洞的起点与终点来建图. 建图时,虫洞起点可以去重. 在建图时 ...

  2. hzwer模拟赛 虫洞

    [题目描述] N个虫洞,M条单向跃迁路径.从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和1单位时间.虫洞有白洞和黑洞之分.设一条跃迁路径两端的虫洞质量差为delta. 1.从白洞跃迁到黑洞,消 ...

  3. BZOJ 1715: [Usaco2006 Dec]Wormholes 虫洞

    Description John在他的农场中闲逛时发现了许多虫洞.虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前).John的每个农场有M条小路(无向边)连接着N ...

  4. BZOJ1715: [Usaco2006 Dec]Wormholes 虫洞

    1715: [Usaco2006 Dec]Wormholes 虫洞 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 475  Solved: 263[Sub ...

  5. 1715: [Usaco2006 Dec]Wormholes 虫洞

    1715: [Usaco2006 Dec]Wormholes 虫洞 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 501  Solved: 278[Sub ...

  6. [P2850][USACO06DEC]虫洞Wormholes (最短路)

    死活调不出来 后来是发现这题建边的原因…… 吐血.jpg 所谓的虫洞传说也就是负边了 然后这里打的spfa和原来的不一样 感觉hzwer大佬的spfa好强啊…… 也更易写一点 贴代码 #include ...

  7. bzoj2262: 平行宇宙与虫洞

    Description 量子力学指出,宇宙并非只有一种形态. 根据量子理论,一件事件发生之后可以产生不同的后果,而所有可能的后果都会形成自己的宇宙. 我们可以把一个宇宙看成一个时间轴,虫洞可以看成不同 ...

  8. SPFA穿越虫洞——负权回路得判断

    poj3259 题目大意:穿越虫洞可以回到过去(时间--)所以能不能让时间倒流呢,就是判断有没有负权回路这次尝试用SPFA算法,也可以复习一下链式前向星 准备工作,队列q,spfa算法得有点就在于这个 ...

  9. bzoj 1715: [Usaco2006 Dec]Wormholes 虫洞 -- spfa判断负环

    1715: [Usaco2006 Dec]Wormholes 虫洞 Time Limit: 5 Sec  Memory Limit: 64 MB 注意第一次加边是双向边第二次是单向边,并且每次询问前数 ...

随机推荐

  1. 【python】利用jieba中文分词进行词频统计

    以下代码对鲁迅的<祝福>进行了词频统计: import io import jieba txt = io.open("zhufu.txt", "r" ...

  2. 多角度让你彻底明白yield语法糖的用法和原理及在C#函数式编程中的作用

    如果大家读过dapper源码,你会发现这内部有很多方法都用到了yield关键词,那yield到底是用来干嘛的,能不能拿掉,拿掉与不拿掉有多大的差别,首先上一段dapper中精简后的Query方法,先让 ...

  3. 多数据源系统接入mybatis-plus, 实现动态数据源、动态事务。

    目录: 实现思想 导入依赖.配置说明 代码实现 问题总结 一.实现思想 接手一个旧系统,SpringBoot 使用的是纯粹的 mybatis ,既没有使用规范的代码生成器,也没有使用 JPA 或者 m ...

  4. SQL基础系列(1)-基本语法--转载w3school

    1.    建原始表 USE [Northwind] GO /****** Object:  Table [dbo].[Persons]    Script Date: 2016/6/8 7:31:5 ...

  5. 【java 数据结构】还不会二叉树?一篇搞定二叉树

    二叉树是我们常见的数据结构之一,在学习二叉树之前我们需要知道什么是树,什么是二叉树,本篇主要讲述了二叉树,以及二叉树的遍历. 你能get到的知识点? 1.树的介绍 2.二叉树的介绍 3.二叉树遍历的四 ...

  6. 差分数组&&定义&&使用方法&&与线段树的区别

    **1.定义**对于一个有n个元素的数组a[n],我们令a[i]-a[i-1]=d[i],且d[1]=a[1]-0=a[1];那么我们将d[i]称为**差分数组**---即记录数组中的每项元素与前一项 ...

  7. 条件变量 condition_variable wait_for

    wait_for(阻塞当前线程,直到条件变量被唤醒,或到指定时限时长后) #include <iostream> #include <atomic> #include < ...

  8. 1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有不同的排列, 如:512234、212345等. 要求:”4”不能在第三位,”3”与”5”不能相连。

    private static String[] mustExistNumber = new String[] { "1", "2", "2" ...

  9. Java面向对象的总结

    面向对象的程序设计 1.面向对象 核心:以类的方式组织代码,以对象的方式封装数据 比喻:也就是说类是没有数据的,给了数据之后的类就是对象 封装 继承 多态 2.方法 a.一个方法只有一个返回值,只有一 ...

  10. linux下的信号量PV操作进阶之路

    一.同步和互斥机制 信号量 互斥锁 同步:指多个任务按照约定的先后次序相互配合来完成一件事情. 比如读线程等待写线程写完之后再去读. 二.信号量-P/V操作 P(s)含义: if(信号量>0) ...