T1:奇怪的电梯(BFS)

题目背景:呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1<=i<=N)上有一个数字 Ki(0<=Ki<=N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5 代表了 Ki(K1=3,K2=3...),从一楼开始。在一楼,按“上”可以到 4楼,按“下”是不起作用的,因为没有-2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?

输入:输入文件共有二行,第一行为三个用空格隔开的正整数,表示 N,A,B(1≤N≤200,1≤A,B≤N),第二行为 N 个用空格隔开的正整数,表示 Ki。

输出:输出文件仅一行,即最少按键次数,若无法到达,则输出-1。
样例:
LIFT.IN
5 1 5
3 3 1 2 5
LIFT.OUT
3

话不多说,直接开讲。

题目思路:在一开始的时候,我看到“上”“下”两种运动方式,我首先想到了二叉树。接着,我又觉得树太麻烦了,就建了个图,然后用Dijkstra算法来求最短路。最终70分 (Wrong Answer,我也不知道为什么)。

正解:从A楼开始,进行BFS,用队列来储存楼层,每次都弹出队头,然后分别向上,向下扩展,更新到达该楼层所用的步数,直至队列为空。(当然,你还可以优化一下,在找到我们需要的目标楼层B后,直接清空队列,跳出循环,节省时间)

但是,你会发现还是超时了,为什么?

因为有环!!!

因为有环!!!

因为有环!!!

重要的事情说三遍。

所以我们就要用一个标记数组来记录楼层的到达情况,意思就是说,假设我们从1楼扩展到了3楼,而三楼并没有用过,也就是没有被扩展到其他楼层,那么我们就将它压入队列,同时标记一下。这样就搞定了。

代码如下:

#include<bits/stdc++.h>
using namespace std;

int c[205];
int n, a, b;
int ans[205];
bool vis[205];
queue <int> q;

void bfs(int root){
    bool temp=false;
    ans[root]=0;
    q.push(root);
    /*代码核心部分*/
    while(!q.empty()){
	int x=q.front();q.pop();//取出队首
        //注意,x+c[x]代表从x楼向上可以到达的楼层
        //注意,x-c[x]代表从x楼向下可以到达的楼层
	if(x+c[x]<=n&&(!vis[c[x]+x])){//将队首向上扩展
	    vis[c[x]+x]=true;//判重
	    ans[x+c[x]]=min(ans[x]+1, ans[x+c[x]]);//更新答案(按按键的次数)
	    if(x+c[x]==b){//因为是广搜,所以第一个到达的肯定是最优的(广搜的无后效性)
    	        temp=true;
	        break;
	    }
	    q.push(x+c[x]);
        }
        if(x-c[x]>=1&&(!vis[x-c[x]])){//将队首向下扩展
            vis[x-c[x]]=true;//同上
            ans[x-c[x]]=min(ans[x-c[x]], ans[x]+1);//同上
            if(x-c[x]==b){//同上
       	        temp=true;
       	        break;
            }
            q.push(x-c[x]);
        }
    }
    if(temp)printf("%d\n", ans[b]);
    else printf("-1\n");
    /**/
}

int main (){
    memset(ans, 0x3f, sizeof(ans));
    scanf ("%d%d%d", &n, &a, &b);
    for(int i=1;i<=n;++i)
    	scanf ("%d", &c[i]);
    bfs(a);
	
    return 0;
}

结语:这是我第一次写博客,我有做的不对的地方,还希望大家多多指点。

谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值