洛谷P3403 跳楼机(最短路)

题目背景

DJL为了避免成为一只咸鱼,来找srwudi学习压代码的技巧。

题目描述

Srwudi的家是一幢h层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi的跳楼机可以采用以下四种方式移动:
向上移动x层;
向上移动y层;
向上移动z层;
回到第一层。
一个月黑风高的大中午,DJL来到了srwudi的家,现在他在srwudi家的第一层,碰巧跳楼机也在第一层。DJL想知道,他可以乘坐跳楼机前往的楼层数。

输入格式

第一行一个整数h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的x, y, z。

输出格式

一行一个整数,表示DJL可以到达的楼层数。

输入

15
4 7 9

输出

9

输入

33333333333
99005 99002 100000

输出

33302114671

提示

可以到达的楼层有:1,5,8,9,10,12,13,14,15
想不出来不要死磕这一题,先看看第三题。。。。
1<=h<=(2^{63})-1
1<=x, y, z<=100000


假设可以到达的楼层为t
我们可以得到一个方程:t=ax+by+cz
如果设t'=by+cz,原方程就变成了t=a'x+t'

f[i]表示表示仅通过操作2和操作3能到达的mod x == i的最小楼层t'
(这里为什么要是最小楼层呢,举个栗子:x == 5时,7和12都满足mod x == 2.而12 = 7 +5,表示12层可以被后面讨论操作一的时候枚举到)
注意:一定要把f[i]的i与f[i]的含义分开,i是f[i] mod x的结果,即i == f[i]%x

状态转移方程:

f[i+y] = f[i]+y;
f[i+z] = f[i]+z;

最短路方程:

dis[b]=dis[a]+Len[a][b];

我们把f[i+z]看做dis[b],把f[i]看做dis[a],将z看做b[(i+z)%x]点和a[i]点之间的边权(y同理)
在这里,(i+y)%x的是防止i+y超过x

建图过程:

void add(int x,y,a){
    End[++tot]=y;
    Len[tot]=a;
    Next[tot]=Last[x];
    Last[x]=tot;
}
……
for(int i=0;i<x;i++){//i从0到x-1
    add(i,(i+y)%x,y);
    add(i,(i+z)%x,z);
}

因为是求最小楼层t',所以用SPFA/Dijkstra解决

通过SPFA/Dijkstra,我们已经求出了仅通过操作2和操作3能到达的mod x == i的最小楼层t'

那么就可以很快的求出还剩几层楼可以跳:(h - f[i]),从而推出剩下仅通过操作一可以跳的楼层个数:(h - f[i])/x(向下取整,因为不能跳超过h层)
再通过ans与它之间的联系算出答案:ans += (h - f[i])/x + 1(剩下可以跳的楼层个数+f[i]本身这一层楼)

一个问题:如果到达f[i]之前有经过其他点,那么从ans += (h - f[i])/x + 1来看,并没有将到达f[i]之前经过的点的个数计入答案,是否存在漏算?
举个栗子来解释:

x=8,y=3,z=5,h=100
f[7]=1+3+3=7

i == 7
ans = ans + (h-f[7])/x+1 == ans + (100-7)/7 +1 == ans + 14
作为“中转站”:第4楼能够达到,却并没有被算在ans里
回过头去看i == 4
ans = ans + (h-f[4])/x+1 == ans + (100 - 4)/7+1 == ans + 9
也就是说,第4楼在算第7楼之前就已经算过了,再次计入ans只会使答案出现重复

最后吐槽一下,这是一道省选难度的Noip模拟题
【因为蹲在墙角所以有回声的小声bbbbbbbbbbbbbbb...】

原文地址:https://www.cnblogs.com/qwqq/p/11251055.html