题目链接:http://codeforces.com/problemset/problem/479/E
题意:
有一栋n层的房子。
还有一个无聊的人在玩电梯,每次玩电梯都会从某一层坐到另外一层。
他初始在a层,然后要玩k次电梯。
这栋楼里还有一个神秘实验室,在b层。
这让他每次坐电梯受到了限制:
当前在x层,然后要坐到y层,则必须满足|x-y|<|x-b|
问你共有多少种坐电梯的方案。
题解:
表示状态:
dp[i][j] = numbers
表示当前在第i层,已经坐了j次电梯,此时的方案数。
找出答案:
ans = ∑ dp[i][k]
如何转移:
若当前在第i层,则移动距离最大为r = |i-b|-1
dp[i-r to i-1][j+1] += dp[i][j]
dp[i+1 to i+r][j+1] += dp[i][j]
注意判断越界。
边界条件:
set dp = 0
dp[a][0] = 1
前缀和/差分优化:
如果直接去做的话,枚举状态要O(N^2),转移要O(N),总复杂度O(N^3)明显超了。
所以考虑将转移变成O(1)的。
因为转移是给一段区间加上同一个值,所以可以用差分去做,转移完之后在求一边前缀和就变成了原值。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 5005 5 #define MAX_K 5005 6 #define MOD 1000000007 7 8 using namespace std; 9 10 int n,a,b,k; 11 int dp[MAX_N][MAX_K]; 12 13 inline int abs(int x) 14 { 15 return x>0 ? x : -x; 16 } 17 18 inline int mod(int x) 19 { 20 return (x%MOD+MOD)%MOD; 21 } 22 23 void sec(int x,int y,int v,int id) 24 { 25 if(x>y || y<=0 || x>n) return; 26 x=max(x,1); x=min(x,n); 27 y=max(y,1); y=min(y,n); 28 dp[x][id]=mod(dp[x][id]+v); 29 dp[y+1][id]=mod(dp[y+1][id]-v); 30 } 31 32 int main() 33 { 34 cin>>n>>a>>b>>k; 35 memset(dp,0,sizeof(dp)); 36 dp[a][0]=1; 37 for(int j=0;j<k;j++) 38 { 39 for(int i=1;i<=n;i++) 40 { 41 int r=abs(i-b)-1; 42 sec(i-r,i-1,dp[i][j],j+1); 43 sec(i+1,i+r,dp[i][j],j+1); 44 } 45 for(int i=1;i<=n;i++) 46 { 47 dp[i][j+1]=mod(dp[i][j+1]+dp[i-1][j+1]); 48 } 49 } 50 int ans=0; 51 for(int i=1;i<=n;i++) ans=mod(ans+dp[i][k]); 52 cout<<ans<<endl; 53 }