洛谷 P1135 搜索

时间:2019-02-16
本文章向大家介绍洛谷 P1135 搜索,主要包括洛谷 P1135 搜索使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

https://www.luogu.org/problemnew/show/P1135

题目描述

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

输入输出格式

输入格式:

 

共二行。

第一行为3个用空格隔开的正整数,表示N,A,B(1≤N≤200,1≤A,B≤N)。

第二行为N个用空格隔开的非负整数,表示Ki​。

 

输出格式:

 

一行,即最少按键次数,若无法到达,则输出−1。

 

输入输出样例

输入样例#1: 复制

5 1 5
3 3 1 2 5

输出样例#1: 复制

3

思路:很容易看出来是搜索,不过肯定是要剪枝的,不然肯定会TLE的。我们用dp[i]表示到达第i层最少需要按的次数,初始化为INF(0x3f3f3f3f),dfs有两个参数,一个是cur,代表当前在第cur层,一个是cnt,代表当前按了cnt次,很明显,如果cnt>=dp[cur]就可以直接return了,没必要继续递归下去。做了这个剪枝,速度就会快很多。当cur=e时更新最优解,就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;

int dp[205];
int a[205];
int n,b,e;

void dfs(int cur,int cnt);

int main()
{
    scanf("%d %d %d",&n,&b,&e);
    memset(dp,INF,sizeof(dp));
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dfs(b,0);
    printf("%d\n",dp[e]==INF?-1:dp[e]);
    return 0;
}

void dfs(int cur,int cnt)
{
    if(cur==e)
    {
        dp[cur]=min(dp[cur],cnt);
        return ;
    }
    if(cnt>=dp[cur])
        return ;
    dp[cur]=cnt;
    if(cur-a[cur]>=1)
        dfs(cur-a[cur],cnt+1);
    if(cur+a[cur]<=n)
        dfs(cur+a[cur],cnt+1);
}

另外一种思路: 上面那个思路是dfs,下面这个思路其实是bfs, 只不过没用一般的bfs的写法。 dp数组同上只不过初始化为-1, bfs的一个很明显的特点就是: 当你第一次到达某个点时所用的步数必定就是到达该点的最少步数, 因此只要第一次访问到结束的层数就可以直接跳出循环(或者说返回)了。 那么实现也很简单:找按0次按钮就能达到的位置(初始位置), 向上、 向下更新dp; 找按1次按钮就能到达的位置……直到访问到了终点位置或者没有必要bfs下去了。(即向上向下都已经访问过了)

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;

int dp[205];    //dp[i]代表走到第i层的最少次数
int a[205];     //a[i]代表第i层电梯上的数字
int n,b,e;

int main()
{
    memset(dp,-1,sizeof(dp));
    scanf("%d %d %d",&n,&b,&e);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dp[b]=0;
    int flag=1;//标记是否有必要继续判断下去
    int cnt=0; //标记当前按了几次电梯
    while(dp[e]==-1&&flag)
    {
        flag=0;
        for(int i=1;i<=n;i++)
        {
            if(dp[i]==cnt)//找到当前情况下的最优解
            {
                if(i+a[i]<=n&&dp[i+a[i]]==-1)//未被访问过且未越界
                {
                    dp[i+a[i]]=cnt+1;
                    flag=1;
                }
                if(i-a[i]>=1&&dp[i-a[i]]==-1)
                {
                    dp[i-a[i]]=cnt+1;
                    flag=1;
                }
            }
        }
        ++cnt;  
    }
    printf("%d\n",dp[e]);
    return 0;
}