洛谷 P3403 跳楼机 题解
题目链接:洛谷 P3403 跳楼机
题目大意
一个 愚笨 聪明的人,闲得慌 坐电梯,电梯只有四种移动方式:
- 向上移动 x x x 层;
- 向上移动 y y y 层;
- 向上移动 z z z 层;
- 回到第一层。
问电梯一共可以去往的楼层数。
写在前面
我表示无语,题目感觉像 d p dp dp 但是设状态有问题,就先打了个暴力。
代码如下(暴力 R E RE RE)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll h,x,y,z,ans=1,bz[1000100];
void dfs(ll t){
if(t+x<=h) if(bz[t+x]==0) bz[t+x]=1,ans++,dfs(t+x);
if(t+y<=h) if(bz[t+y]==0) bz[t+y]=1,ans++,dfs(t+y);
if(t+z<=h) if(bz[t+z]==0) bz[t+z]=1,ans++,dfs(t+z);
}
int main()
{
scanf("%lld",&h);
scanf("%lld%lld%lld",&x,&y,&z);
dfs(1);
printf("%lld",ans);
return 0;
}
正解
其实这是一道伪装成数论的图论,非常恶心。正解是最短路,但是是同余最短路(模板)。
同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。同余最短路的状态转移通常是这样的 f ( i + y ) = f ( i ) + y f(i+y) = f(i) + y f(i+y)=f(i)+y,类似单源最短路中 f ( v ) = f ( u ) + e d g e ( u , v ) f(v) = f(u) +edge(u,v) f(v)=f(u)+edge(u,v)。(借鉴了OI_WIKI)
详见 OI-WIKI :同余最短路
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF=1e18+100;
const int MAXN=100100;
ll hd[MAXN<<1],nxt[MAXN<<1],to[MAXN<<1],len[MAXN<<1],d[MAXN],vis[MAXN],a[MAXN];
ll x,y,z,h,ans,cnt,sum,ff;
ll mymax(int a1,int b)
{
return a1>b?a1:b;
}
void add(ll u,ll v,ll l)
{
++cnt;
to[cnt]=v;
len[cnt]=l;
nxt[cnt]=hd[u];
hd[u]=cnt;
}
void spfa() //其实是栈优化,不是队列优化,复杂度有点问题,不建议使用
{
d[1]=1;
vis[1]=1;
a[++ff]=1;
while(ff!=0)
{
ll u=a[ff];
ff--;
vis[u]=0;
for(ll i=hd[u];i;i=nxt[i])
{
ll v=to[i],l=len[i];
if(d[v]>d[u]+l)
{
d[v]=d[u]+l;
if(vis[v]==0)
{
a[++ff]=v;
vis[v]=1;
}
}
}
}
}
int main()
{
for(int i=0;i<=100000;i++) d[i]=INF;
scanf("%lld",&h);
scanf("%lld%lld%lld",&x,&y,&z);
if(y<x) swap(x,y);
if(z<y) swap(y,z);
if(x==1)
{
ans=h;
}
else
{
// ll maxn=0;
for(int i=0;i<x;i++)
{
add(i,(i+z)%x,z);
add(i,(i+y)%x,y);
// maxn=mymax((i+z)%x,(i+y)%x);
}
// for(int i=0;i<=maxn;i++) d[i]=INF;
spfa();
for(int i=0;i<x;i++)
{
if(h>=d[i])
{
ans+=(h-d[i])/x+1;
}
}
}
printf("%lld",ans);
return 0;
}