CF_Round274_Div1C_Riding in a lift_动态规划

又过了十二点睡不了,让我早睡一次吧。


题意:

你在一个大楼里坐电梯玩,楼层从1到n编号,你一开始在a层,b层不能去,由于某些神秘的原因,你从x层可以到达的楼层y需要满足|x-y|<|x-b|,你一共要坐k次电梯,问有多少种做法?


Input

The first line of the input contains four space-separated integers n, a, b, k (2 ≤ n ≤ 5000, 1 ≤ k ≤ 5000, 1 ≤ a, b ≤ n, a ≠ b).

Output

Print a single integer — the remainder after dividing the sought number of sequences by 1000000007 (109 + 7).


dp状态转移方程不难想,无非是从上一次乘坐可能的所有情况往这次推,dp数组记录当前点有多少种可能来源,但是k次加n层,已经需要O(NK)的时间复杂度了,单点递推不能满足效率要求,但是注意到可以递推的点是满足一个线性不等式,所以某个点的上一次乘坐地点肯定是线(扣掉了单个的点),那样就可以用前缀和的技巧,开一个数组记录从头到当前楼层每层dp值之和,而更新这个数组是和更新dp平行进行的,复杂度仍然是O(nk)。

具体递推公式不再赘述,解不等式即可。

比赛的时候已经写完了代码,但是递推公式的代码表述有点小问题,时间不够了没改完,今天主要是B题耗时太大。


代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 5010
#define mod 1000000007
#define mxk 5010
long long dp[2][mxn];
long long sum[2][mxn];
int a,b,n,k;
int main(){
    while(scanf("%d%d%d%d",&n,&a,&b,&k)!=EOF){
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        dp[0][a]=1;
        for(int i=a;i<=n;++i)
            sum[0][i]=1;
        for(int i=1;i<=k;++i)
            for(int j=1;j<=n;++j){
                if(j<b){
                    int tem=(b+j)/2;
                    if((b+j)%2==0)  --tem;
                    dp[i%2][j]=(sum[(i+1)%2][tem]-dp[(i+1)%2][j]+mod)%mod;
                }
                else if(j>b)    dp[i%2][j]=(sum[(i+1)%2][n]-sum[(i+1)%2][(j+b)/2]-dp[(i+1)%2][j]+mod)%mod;
                sum[i%2][j]=(sum[i%2][j-1]+dp[i%2][j]+mod)%mod;
            }
        cout<<(sum[k%2][n]+mod)%mod<<endl;
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值