题解【2.23考试T3】val

3. val
【题目描述】
  这是一道传统题,源代码的文件名为 val.cpp/c/pas。
  有一个值初始为 0,接下来 n 次你可以令其在之前基础上+2 或+1 或-1。你需要保证,这个值在整个过程中达到的最大值减去达到的最小值不大于 k,求方案数,模 1,000,000,007。
【输入格式】
  从 val.in 中读入。
  仅一行,两个空格隔开的正整数 n 和 k。
【输出格式】
  输出到 val.out 中。
  仅一行,一个非负整数,表示方案数对 1,000,000,007 取模后的结果。
【输入样例 A】
3 2
【输出样例 A】
11
【输入样例 B】
233 99
【输出样例 B】
316461264
【评分标准】
对于 10%的数据,n,k<=15;
对于 30%的数据,n,k<=75;
对于 50%的数据,n,k<=300;
对于另 10%的数据,k=1;
对于 100%的数据,n,k<=5,000。
时间限制 2s,空间限制 512MB。

 

题解:

  这道题我一开始想的是直接O(3n)暴力搜索每一种情况,并记录最大最小值依次判断,但是这样做只能得20分。

  考虑到本题中无后效性的特点,再想一想有最优子结构,于是想到此题正解是DP。

  枚举最小值是 d,则只需要限制达到的值始终在 d 和 d+k 之间,且保证达到过 d 即可,于是每次枚举还需要一个 O(nk)的 dp。注意到这可以认为是从-d 出发,达到的值始终在 0 到k 之间,且保证过达到 0。这样子就不需要枚举 d,直接做一次 dp 就可以了。

 

  代码(std):

 1 #include <bits/stdc++.h>
 2 #define rep(i,l,r) for(int i=l;i<=r;i++)
 3 #define per(i,r,l) for(int i=r;i>=l;i--)
 4 #define mo 1000000007
 5 #define N 5005
 6 int n,k,dp[N][N][2];
 7 int main()
 8 {
 9     char fni[]="val.in",fno[]="val.out";
10     freopen(fni,"r",stdin);
11     freopen(fno,"w",stdout);
12     scanf("%d%d",&n,&k);
13     memset(dp,0,sizeof(dp));
14     rep(i,0,k) dp[0][i][!i]=1;
15     rep(i,1,n)
16     {
17         rep(j,0,k) rep(t,0,1)
18         {
19             if(j>=2) (dp[i][j][t]+=dp[i-1][j-2][t])%=mo;
20             if(j>=1) (dp[i][j][t]+=dp[i-1][j-1][t])%=mo;
21             if(j<k) (dp[i][j][t]+=dp[i-1][j+1][t])%=mo;
22         }
23         (dp[i][0][1]+=dp[i][0][0])%=mo;
24         dp[i][0][0]=0;
25     }
26     int ans=0;
27     rep(i,0,k) (ans+=dp[n][i][1])%=mo;
28     printf("%d\n",ans);
29     fclose(stdin);
30     fclose(stdout);
31 }

 

posted @ 2019-02-23 20:53  csxsi  阅读(167)  评论(0编辑  收藏  举报