您的位置:首页 > 其它

CF_Round274_Div1C_Riding in a lift_动态规划

2014-10-20 00:54 411 查看
又过了十二点睡不了,让我早睡一次吧。

题意:

你在一个大楼里坐电梯玩,楼层从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]
-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]
+mod)%mod<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: