跳楼机
题目背景
DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。
题目描述
Srwudi 的家是一幢 h h h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi 的跳楼机可以采用以下四种方式移动:
- 向上移动 x x x 层;
- 向上移动 y y y 层;
- 向上移动 z z z 层;
- 回到第一层。
一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。
输入格式
第一行一个整数 h h h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的 x , y , z x, y, z x,y,z。
输出格式
一行一个整数,表示 DJL 可以到达的楼层数。
样例 #1
样例输入 #1
15
4 7 9
样例输出 #1
9
样例 #2
样例输入 #2
33333333333
99005 99002 100000
样例输出 #2
33302114671
提示
可以到达的楼层有: 1 , 5 , 8 , 9 , 10 , 12 , 13 , 14 , 15 1,5,8,9,10,12,13,14,15 1,5,8,9,10,12,13,14,15。
1 ≤ h ≤ 2 63 − 1 1 \le h \le 2^{63}-1 1≤h≤263−1, 1 ≤ x , y , z ≤ 1 0 5 1 \le x,y,z \le 10^5 1≤x,y,z≤105。
思路
- 总的来说,这道题就是有3个变量,且所有的朝向都是向上的,此时我们可以定二移一,就如上图把状态表示设为那个。
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define int long long
using namespace std;
const int N = 1e6+10,M = 2*N;
int e[M],ne[M],h[N],e2[M],idx;
bool st[N];
int dist[N];
int n;
int x,y,z;
/*f(i)表示仅通过2操作和3操作能到达的 mod x = i 的最小楼层
显然
f(i+y)=f(i)+y
f(i+z)=f(i)+z;
而最短路的求法
f(y)=f(x)+edge(i);
让(i+y)和(i+z)成为点
让y,z成为权值
从而算出f(i)
*/
void add(int a,int b,int c){
e[idx]=b,e2[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
memset(dist,0x3f3f3f3f,sizeof dist);
queue<int>q;
q.push(1);
st[1]=true;
dist[1]=1;
while(q.size()){
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+e2[i]){
dist[j]=dist[t]+e2[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
signed main(){
cin>>n>>x>>y>>z;
//同余最短路
if(x==1||y==1||z==1){
cout<<n;
return 0;
}
memset(h,-1,sizeof h);
for(int i=0;i<x;i++){
add(i,(i+y)%x,y);
add(i,(i+z)%x,z);
}
spfa();
int ans=0;
for(int i=0;i<x;i++){
if(dist[i]<=n){
ans+=(n-dist[i])/x+1;
}
}
cout<<ans;
return 0;
}