动态规划-今日头条-房间跳跃次数

题目描述

存在n+1个房间,每个房间依次为房间1 2 3...i,每个房间都存在一个传送门,i房间的传送门可以把人传送到房间pi(1<=pi<=i),现在路人甲从房间1开始出发(当前房间1即第一次访问),每次移动他有两种移动策略:
    A. 如果访问过当前房间 i 偶数次,那么下一次移动到房间i+1;
    B. 如果访问过当前房间 i 奇数次,那么移动到房间pi;
现在路人甲想知道移动到房间n+1一共需要多少次移动;

输入描述:

第一行包括一个数字n(30%数据1<=n<=100,100%数据 1<=n<=1000),表示房间的数量,接下来一行存在n个数字 pi(1<=pi<=i), pi表示从房间i可以传送到房间pi。

输出描述:

输出一行数字,表示最终移动的次数,最终结果需要对1000000007 (10e9 + 7) 取模。

示例1

输入

2
1 2

输出

4

说明

开始从房间1 只访问一次所以只能跳到p1即 房间1, 之后采用策略A跳到房间2,房间2这时访问了一次因此采用策略B跳到房间2,之后采用策略A跳到房间3,因此到达房间3需要 4 步操作。

解析与总结

1.其实一开始我也没有思路,根源是我没有认真审题,第二是理解不透彻没有想通,题目条件pi(1<=pi<=i)是题目的关键,此题目如果不采用动态规划算法会超时。
2.我们可以将一排房间排列如下图所示,假设1房间为最前,i房间为最后,路人甲移动的目的就是移动到最后的房间,即第n个房间的下一个房间。
3.注意到条件pi(1<=pi<=i),由题目可知,如果路人甲目前处于房间i,那么他只能向后跳跃一格,或者跳跃至pi房间,而pi<=i,也就是说只有是i房间访问过偶数次,路人甲才有可能向后跳跃并到达终点。这句话意味着传送门不可能把你往后面的门传,如果你想向后走,那么你只能访问该房间偶数次;

4.下面是重点,也是理解本题思路的关键,假设你现在第一次到达i门,你觉得前面i - 1个房子你都访问了多少次?每个房子访问了多少次我不知道,但是我知道每个房子访问的次数都是偶数!这一点很重要。仔细想想,假如前面i - 1中有一个房子的访问次数不是偶数次,那么,你不可能向后走,更不可能走到i门。(如果是奇数就传送到前面的房间去了呀)

5.下面开始建模,找到动态规划递推关系,我们假设dp[i]为到达第i个房间,并且第一次进入次数为偶数,此时所用的移动次数。

6.下面根据图解来列递推关系,dp[i-1]为第一个偶数次到达i-1房间所移动的次数,那么此时他将会传送到后一个房间,也就是i房间,然而此时路人甲是第一次到达i房间,并不是偶数次到达,并不满足dp[i],因此我们先列一个关系:

dp[i] = dp[i - 1] + 1                                     //这里dp[i]是暂时的,还不是真正的dp[i]

7.此时路人甲是第一次到达i房间,他会传送到pi房间去,假设pi=k,由于i房间之前的房间都被访问过偶数次,此次到k房间,又是奇数次访问k房间。再列一个关系:

dp[i] = dp[i] + 1                                          //传送到pi房间去,也算移动一步

8.现在回归到这样一个问题了,就是现在处于奇数次访问k(k<=i)房间了,现在还需要移动多少步才能又回到i房间呢,我们假设x[k]为第一次到达k房间所用得步数,那么

x[k] = dp[k-1] + 1

那么从dp[k-1]移动到dp[i-1]需要移动多少步呢,当然是dp[i-1] - dp[k-1],

换句话说,根据这个关系,当处于奇数次访问k房间到再次访问i房间,还需要走dp[i-1] - dp[k-1]步。(因为此时k-1到k只需要一步,i-1到i也只需要一步,相互抵消)

9.综上,我们可以累加得到dp[i]和dp[i-1]的关系,此时dp[i-1]为第一次偶数次到达i-1房间的步数,dp[i]为第一次偶数次到达i房间的次数,递推关系如下:

dp[i] = dp[i-1] + 1 + 1 + dp[i-1]-dp[k-1] = 2 * dp[i-1] - dp[k-1] + 2

替换k即可:dp[i] = 2 * dp[i-1] - dp[ pi[i] - 1 ] + 2

10.下面以后端Java语言为例,贴出我的代码,根据题意,dp[1]输出1,根据递推关系,dp[0]=-1

import java.util.*;

public class Main {
    public static void main(String[] args) {
        int MOD = 1000000007;
        Scanner sc = new Scanner(System.in);
        int rooms = sc.nextInt();
        //房间号为1-rooms
        int[] pi = new int[rooms+1];
        for(int i=1 ; i<=rooms ; i++) {
            pi[i] = sc.nextInt();
        }
        int[] dp = new int[rooms+1];
        //难道不是输出2吗,奇怪
        if(rooms==1) {
            //题意不明确,既然rooms==1时,结果输出1,那么只能dp[0]=-1了
            System.out.println(1);
            return;
        }
        //p1只能是1,因为1<=pi<=i
        dp[0] = -1;
        dp[1] = 1;
        int k=2;
        while(k <= rooms) {
            //动态规划,递推关系
            dp[k] = ((2* dp[k-1])%MOD - dp[pi[k]-1] +2 + MOD)%MOD;
            k++;
        }
        System.out.println(dp[rooms] + 1);
    }
}

11.其实我当时第一做法没想到动态规划,也贴出我的代码吧,通过率60%,算法超时了,现在想想用动态规划也没那么难,想通了理解了就还好

import java.util.*;

public class Main {
    /* 此种方法超时*/
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int rooms = sc.nextInt();
        int[] pi = new int[rooms];
        for(int i=0 ; i<rooms ; i++) {
            pi[i] = sc.nextInt();
        }
        int[] step = new int[rooms+1];
        step[0] = 1;
        int index = 0;
        int count = 0;
        while(index!=rooms) {
            if(step[index]%2==0) {
                index++;
            } else {
                //基于0索引,所以-1
                index = pi[index]-1;
            }
            step[index]++;
            step[index] %= 2;
            count++;
            count %= 1000000007;
        }
        System.out.println(count);
    }
}

最后加上我的思路来源:https://blog.csdn.net/flushhip/article/details/79458502

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值