跳楼机(建图难点+同余最短路)

跳楼机

题目背景

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

题目描述

Srwudi 的家是一幢 h h h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。

经过改造,srwudi 的跳楼机可以采用以下四种方式移动:

  1. 向上移动 x x x 层;
  2. 向上移动 y y y 层;
  3. 向上移动 z z z 层;
  4. 回到第一层。

一个月黑风高的大中午,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 1h2631 1 ≤ x , y , z ≤ 1 0 5 1 \le x,y,z \le 10^5 1x,y,z105

思路

sbxrf,祝我蓝桥杯国赛至少国三

  • 总的来说,这道题就是有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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值