P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目在这里
一、dfs
看题目,感觉简单,直接开搜!
初次判断需要留意的点:
1.向下坐电梯,层数小于1;向上坐电梯,层数大于最高层。用两个if就可以解决。
2.可能有死循环,比如3 1 2 0 2,从3到4层,可能在3和5之间一直往返(每次到3都先进入上楼的路径)。所以需要一个数组判断当前楼层有没有来过。
3.如果已经到了,就更新最小值。
于是有了以下代码。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int l[205];
int st[205] ={0};
int n,a,b,minn=INT_MAX,cnt=0;
void dfs(int d) {
if(d==b){
minn=min(cnt,minn);
return;
}
if(cnt>minn){
return;
}
if(d+l[d]<=n&&st[d]==0){
cnt++;
st[d]=1;
dfs(d+l[d]);
st[d]=0;
cnt--;
}
if(d-l[d]>=1&&st[d]==0){
cnt++;
st[d]=1;
dfs(d-l[d]) ;
st[d]=0;
cnt--;
}
}
int main() {
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
cin>>l[i];
}
dfs(a);
if(minn!=INT_MAX){
cout<<minn;
}else{
cout<<-1;
}
return 0;
}
其实cnt完全可以放在dfs里,但是不想改了TAT
但是提交到洛谷只有可怜的52分!一片tle!
既然没wa只有tle,自己的算法应该是没啥问题的,那就是还有没剪掉的枝。
思索了一下,发现可以加入条件:如果当前路径的步数已经超过了之前统计过的最少步数,那这肯定不是最短路,直接返回。插入代码:
if(cnt>minn){
return;
}
从52爬到了64分...难道是还有能剪掉的情况?
看了其他人的代码后了然:可以加入数组,记录来到当前楼层需要的最少步数。如果来到此楼,但是之前已经存在花费比这更少的步数就能到达这里的情况,那说明中间肯定绕了路,不是最短路。
7 2 7
2 1 2 0 2 0 0
如果是第一种算法:
2-3-5-7(最短路
2-1-3-5-7(也能到但是不采用
如果第二种:
2-3-5-7(最短路
2-1-此时本来要到3,但是之前的路径里我们已经到过3,肯定绕路了
比第一种情况少了后面的357,所有快一点。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
int l[205];
int f[205]={0};
int n,a,b,minn=INT_MAX,cnt=0;
void dfs(int d) {
f[d]=cnt;
if(cnt>minn){
return;
}
if(d==b){
minn=min(cnt,minn);
return;
}
if(d+l[d]<=n&&cnt+1<f[d+l[d]]){
cnt++;
dfs(d+l[d]);
cnt--;
}
if(d-l[d]>=1&&cnt+1<f[d-l[d]]){
cnt++;
dfs(d-l[d]);
cnt--;
}
}
int main() {
cin>>n>>a>>b;
memset(f,127,sizeof(f));
for(int i=1;i<=n;i++){
cin>>l[i];
}
dfs(a);
if(minn!=INT_MAX){
cout<<minn;
}else{
cout<<-1;
}
return 0;
}
终极版本!可以100分了,应该快了很多。
二、新学的bfs
看题解说这道题可以用bfs做,因为这是一个最短路问题。去b站观摩了一下队列bfs的算法动画,被o(n)的复杂度深深折服。。。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
struct Q{
int d;
int cnt;
};
Q m;
int l[205];
int f[205]={0};
int n,a,b;
int main() {
cin>>n>>a>>b;
for(int i=1;i<=n;i++){
cin>>l[i];
}
queue<Q>q;
q.push({a,0});
while(!q.empty()){
m=q.front();
if(m.d==b){
cout<<m.cnt;
return 0;
}
q.pop();
if(m.d+l[m.d]<=n&&f[m.d+l[m.d]]==0){
q.push({m.d+l[m.d],m.cnt+1});
f[m.d+l[m.d]]=1;
}
if(m.d-l[m.d]>=1&&f[m.d-l[m.d]]==0){
q.push({m.d-l[m.d],m.cnt+1});
f[m.d-l[m.d]]=1;
}
}
cout<<-1;
return 0;
}
dfs和bfs明明只差一个字母,居然如此不一样,一个是递归一个是队列!
队列左侧为父节点,在右侧加入对应的子节点,遍历一次就足够。