跳楼机
https://www.luogu.org/problemnew/show/P3403 (题目链接)
题目
Srwudi的家是一幢h层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi的跳楼机可以采用以下四种方式移动:
1.向上移动x层;
2.向上移动y层;
3.向上移动z层;
4.回到第一层。
一个月黑风高的大中午,DJL来到了srwudi的家,现在他在srwudi家的第一层,碰巧跳楼机也在第一层。DJL想知道,他可以乘坐跳楼机前往的楼层数。
简化版题目:给你一个数字h,三个数字x,y,z。求出三个数字组合出的小于等于h的数字种类。
输入输出格式
输入格式:
第一行一个整数h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的x, y, z。
输出格式:
一行一个整数,表示DJL可以到达的楼层数。
输入输出样例
输入样例
#1:
15
4 7 9
输出样例
#1:
9
输入样例
#2:
33333333333
99005 99002 100000
输出样例
#2:
33302114671
思路
首先因为三个数字的不太好思考,所以可以先考虑两个数字的结果。
两个数字(x,y):
1.可以把h分成两个部分(一部分是x,一部分是y),在这里姑且用x为填充物
2.可以定义一个d[i]数组记录由y组成的数字在mod x的情况下余数为i的最小值(这里设置为最小值是为了避免d数组的重复)
3.统计数字数。ans+=(h-d[i])/x+1【/x是找到可以填充多少个x,+1是因为可能会有x不填充的情况】
之后,考虑 三个数字(x,y,z) 的情况的情况:
1.【同上】可以把h分成两个部分(一部分是x,一部分是y,z),在这里姑且用x为填充物
【划重点】 2.d[i]数组的意义同上,但是这个时候要如何找到两个数字(y,z)在mod x时的最小的余数??而且还要保证这个y,z组合的 任意性 和 随机性 ,而且要保证d[i]是最小的(这里需要冷静思考。)【如果d[i]是最小的的话就不会有重复的情况出现】
此时,可以考虑最短路了,说实话,我没想到 ,用y,z建边权,以i为下标,此时去跑最短路!!【正解的精简干练??】
3.统计数字数。同上。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mm=1e5+100;
ll h,x,y,z,ans;
int num=0;
ll d[mm];
bool v[mm];
queue<int> q;
struct edge
{
int to,nex;
ll ver;
}e[2*mm];
int last[mm];
void add(int x,int y,ll c)
{
e[++num].to=y; e[num].ver=c;
e[num].nex=last[x]; last[x]=num;
}
void spfa()//求单源最短路径,参考思路上的内容
{
memset(d,0x3f3f3f3f,sizeof(d));
memset(v,0,sizeof(v));
d[0]=1;v[0]=1;
q.push(0);
while(q.size())
{
int xx=q.front(); q.pop();
v[xx]=0;
for(int i=last[xx];i;i=e[i].nex)
{
int y=e[i].to;
ll z=e[i].ver;
if(d[y]>d[xx]+z)
{
d[y]=d[xx]+z;
if(!v[y])
q.push(y),v[y]=1;
}
}
}
}
int main()
{
// freopen("srwudi.in","r",stdin);
// freopen("srwudi.out","w",stdout);
cin>>h>>x>>y>>z;//以x为最后的填充对象
num=1;
for(int i=0;i<x;i++)//建边
{
add(i,(i+y)%x,y);
add(i,(i+z)%x,z);
}
spfa();
for(int i=0;i<x;i++)
if(d[i]<=h)
ans+=(h-d[i])/x+1;//还有可能就是(h-d[i])不被x填充的情况,所以在这里要+1
cout<<ans<<endl;
return 0;
}
完……