首页 > 试题广场 >

附加题

[编程题]附加题
  • 热度指数:422 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
存在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 步操作。
#解析:https://blog.csdn.net/weixin_42001089/article/details/87114410
n = int(input())
home = [int(e)-1 for e in input().split()]
dp = [0 for _ in range(n+1)]
dp[1] = 2
for i in range(2,n+1):
    dp[i] = 2*dp[i-1] - dp[home[i-1]]+2
print(dp[n]%1000000007)

发表于 2019-02-18 12:34:02 回复(1)
dp[i]表示从 1 开始出发到i需要移动的次数,则
dp[i]=dp[i-1]+f(i-1,i)=dp[i-1]+1+f(p[i-1],i-1)+1=dp[i-1]+1+ ( dp[i-1]-dp[ p[i-1] ] ) +1;
其中f( j , i )表示当前访问过 j 奇数次后从  j 出发到 i 需要移动的次数,
就等于当前访问过 j 1次后从 j 出发到 i 需要移动的次数,
因此,由dp[i]=dp[j]+f( j , i)可得f( j , i)=dp[i]-dp[j];
#include <iostream>
 
usingnamespacestd;
 
longlongconstm=1000000007;
intmain()
{
    intn;
    while(cin>>n)
    {
        intp[n+1];
        p[0]=0;
        for(inti=1; i<=n; i++)
            cin>>p[i];
        longlongdp[n+1];
        for(inti=0; i<=n; i++)
        {
            dp[i]=0;
        }
        dp[2]=2;
        for(inti=3; i<=n; i++)
        {
            dp[i]=(dp[i-1]+1+dp[i-1]-dp[p[i-1]]+1)%m;
        }
        cout << ((dp[n]+1+dp[n]-dp[p[n]]+1)%m+m)%m << endl;
    }
}
发表于 2018-03-29 17:25:40 回复(2)
分享一个更符合人类思考方式的解法:状态机模型
dp[i][偶] 表示以偶数次到达房间 i 的最小步长

dp[i][奇] 表示以奇数次到达房间 i 的最小步长

dp[i][奇] = dp[i - 1][偶] + 1 这个是很容易想到的,到达i - 1 最小步长加一即为以奇数次状态到达i的最小步长

dp[i][偶] = dp[i][奇] + 重复子问题时间
这里很直观,想偶数次到达 i 必须先奇数次到达,然后被传送到过去,之后的重复子问题是唯一需要思考的点
重复子问题 = 1 + dp[i - 1][偶] + 1 - dp[x][奇]
第一个 1 是从i走一步回到之前某个位置 x,显然,被传送回去之后需要再次达到dp[i - 1][偶]的状态走一步到达 目标房间 i , 这个子问题是的答案是计算过的,但是起始的状态是dp[x][奇],因此要把这部分消耗减去。(之所以是dp[x][奇] 而不是 dp[x][偶] 是因为传送门只会往前跳,若想访问 i ,到达i时前面的房间必然被访问了偶数次, 否则无法前进,因此下次到达的是奇数状态)

整理: dp[i][奇] = dp[i - 1][偶] + 1
            dp[i][偶] = 2dp[i][奇] + 1 - dp[x][奇]
初始化: dp[1][奇] = 0, dp[1][偶] = 1
返回:dp[n + 1][奇]
def helper(nums, n):
    dp = [[0, 0] for _ in range(n + 2)]
    dp[1][0] = 1
    dp[1][1] = 0
    for i in range(2, n + 2):
        dp[i][1] = dp[i - 1][0] + 1
        if i == n + 1:
            break    
        dp[i][0] = 2 * dp[i][1] + 1 - dp[nums[i - 1]][1]
        
    return dp[n + 1][1] % 1000000007
    




n = int(input())
home = list(map(int, input().strip().split()))
print(helper(home, n))


发表于 2022-02-20 13:15:57 回复(0)
//1000000007都是为了避免溢出,实际上是dp[i+1]=dp[i]-dp[pi-1]+2+dp[i]
#include <iostream>
usingnamespacestd;
intmain() {
    intn;
    while(cin>>n){
        intdp[n+1]={0};
        intpi,tmp;
        for(inti=0;i<n;i++){
            cin>>pi;
            tmp=dp[i]<dp[pi-1]?dp[i]+1000000007:dp[i];
            dp[i+1]=(2*tmp-dp[pi-1]+2)%1000000007;
        }
        cout<<dp[n]%1000000007<<endl;
    }
}

发表于 2019-07-08 11:36:19 回复(0)
'''
上面BPSK大佬有些我自己编的事例通不过,比如 (3 \n 1 2)做了一点修改。
'''
n = int(input())
home = [int(e)-1 for e in input().split()]
dp = [0 for _ in range(n+1)]
dp[1] = 2
id_pi = 0
for i in range(2,n+1):
    if id_pi<len(home)-1:
        if home[id_pi+1]<=i:
            id_pi+=1
    dp[i] = 2*dp[i-1] - dp[home[id_pi]]+2
print(dp[n]%1000000007)


发表于 2019-04-13 18:05:28 回复(0)
#f(n)=2f(n-1)-f(a[n-1-1])+2   不晓得为什么不递归直接用标记变量平推为什么通过率只有60%,难道是n个标记变量太费时间、、、、
n=int(input())
a=list(map(int,input().split()))
def ff(x):
    if x==1:
        m=0
    elif x==2:
        m=2
    else:
        m=2*ff(x-1)-ff(a[x-1-1])+2
    return m
print(ff(n+1)%1000000007)
发表于 2018-10-05 19:15:52 回复(1)
#递归求解,f(i+1)= 2 * f(i) - f(p(i)) + 2 n = int(input())

A = [0] + [int(i) for i in input().split()]

i = 0

B = (len(A)+2)*[0]
cnt = 0


for i in range(3,n+2):
    B[1] = 0
    B[2] = 2
    B[i] = 2*B[i-1] - B[A[i-1]] + 2

print(B[n+1]%1000000007)
    

发表于 2018-09-20 00:06:41 回复(0)

此题是一个分析规律,总结递推公式的问题。
设首次到达i号位置所需的次数为f(i)。由题设规则容易知道,到达某个点后,若到达次数为奇数,一定会从该点再次返回到该点以前的位置(pi <= i),那么此后一定会再次到达该点,此时到达次数为偶数,跳转到i + 1,显然到达点i时,点1<=j<i的访问次数均为偶数次。
考虑第1次到达i点,已经使用的步数为f(i),跳转到pi点使用步数1,此时pi点已经访问过的次数为偶数,那么再次到达pi的访问次数为奇数次,则再次重复从pi到i的访问路径,显然这条访问路径的步数为f(i - 1) - f(pi)。然后再次到达i点,此时为第二次到达,转移到i+1,在需要1次。总的步数即为f(i) + (1 + f(i - 1) - f(pi)) + 1 = 2 * f(i) - f(p(i)) + 2。由此递推公式,即可依次计算出i = 1, 2, ..., n, n + 1

发表于 2018-02-21 11:48:20 回复(1)