P1135 奇怪的电梯 题解

博客探讨了一道编程题目“奇怪的电梯”,指出解决问题的关键在于采用搜索策略。文章提到初始的朴素DFS会遇到时间限制问题,通过避免重复状态、使用步数桶记录等方法仍然无法有效解决。最终,作者提出采用记忆化搜索的解决方案,通过维护每个位置的最短步数,并在步数超过已知最短步数时停止搜索,从而达到O(n)的时间复杂度。文章还提醒注意特殊情况A=B的情况。

博客园同步

原题链接

简要题意:

给定 a i a_i ai,求从 A A A 节点走到 B B B 节点的最小步数。若当前在 i i i 节点,则可以一步到达 i − a i i-a_i iai i + a i i+a_i i+ai 节点。(如果不合法则不走)

首先这题肯定是搜索,具体怎么搜因人而异。

因为我比较喜欢朴素的 dfs \texttt{dfs} dfs,因此就说说做法。

显然,如果你打下一个这样的搜索:

inline void dfs(int dep,int bs) {
	if(dep<1 || dep>n) return;
	if(bs==B) {ans=min(ans,bs);return;}
	dfs(dep+a[dep],bs+1);
	dfs(dep-a[dep],bs+1);
} 

你觉得没有任何问题,但是交上去 TLE \texttt{TLE} TLE 了。

给出一组数据 hack \text{hack} hack 掉它:

5 3 4
2 1 2 1 2

然后你的程序彻底陷入死循环了。。。

然后,你机智地说:好,那么我记录上一次的位置,就可以避免重复了!

inline void dfs(int dep,int bs,int last) {
	if(dep<1 || dep>n) return;
	if(bs==B) {ans=min(ans,bs);return;}
        if(dep+a[dep] != last) dfs(dep+a[dep],bs+1);
	if(dep-a[dep] != last) dfs(dep-a[dep],bs+1);
} 

好,交上去还是 TLE \texttt{TLE} TLE 了。

再送你一组数据强力 hack \text{hack} hack

5 1 5
1 2 1 3 1

然后你的程序就是这样的:

1 → 2 → 4 → 1 → 2 → 4 → ⋯ 1 \rightarrow 2 \rightarrow 4 \rightarrow1 \rightarrow2 \rightarrow4 \rightarrow \cdots 124124

仍然不行了。

那你说,好,我用一个桶记录走过的步数,不重复走。

然后交上去 WA \texttt{WA} WA 了。再送你两组 hack \text{hack} hack 数据:

假设你的程序优先往下走:

5 3 5
4 1 2 1 2

然后你用 2 2 2 步走到终点,发现 1 , 3 , 5 1,3,5 1,3,5 都走过了,然后咕了。

如果优先向上走:

5 3 1
2 1 2 1 4

这不一样的道理?

那你说,这怎么办?

我:显然记忆化答案!

f i f_i fi 表示 当前 走到第 i i i 层的最少步数。

如果当前步数 ≥ f i \geq f_i fi 就可以直接停止。

再做一遍即可。

时间复杂度: O ( n ) O(n) O(n).

实际得分: 90 p t s 90pts 90pts.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,A,B,a[201];
int f[201];

inline void dfs(int dep,int bs) {
	if(dep<1 || dep>n) return;
	if(f[dep] && f[dep]<=bs) return;
	f[dep]=bs;
	dfs(dep+a[dep],bs+1);
	dfs(dep-a[dep],bs+1);
} 

int main(){
	n=read(),A=read(),B=read();
	for(int i=1;i<=n;i++) a[i]=read();
	dfs(A,0);
	if(A!=B && !f[B]) puts("-1"); //走不到
	else printf("%d\n",f[B]); //直接走
	return 0;
}

没有一点点的问题啊? 其实我自己也中了这个坑

那么,最后送你组 hack \text{hack} hack 数据:

5 1 1
0 0 0 0 0

你的程序输出了 1 1 1,所以 A = B A=B A=B 的直接处理掉!

实际得分: 100 p t s 100pts 100pts.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,A,B,a[201];
int f[201];

inline void dfs(int dep,int bs) {
	if(dep<1 || dep>n) return;
	if(f[dep] && f[dep]<=bs) return;
	f[dep]=bs;
	dfs(dep+a[dep],bs+1);
	dfs(dep-a[dep],bs+1);
} 

int main(){
	n=read(),A=read(),B=read();
	if(A==B) {puts("0");return 0;}
	for(int i=1;i<=n;i++) a[i]=read();
	dfs(A,0);
	if(!f[B]) puts("-1");
	else printf("%d\n",f[B]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值