1. 程式人生 > >題解【2.23考試T3】val

題解【2.23考試T3】val

-- 枚舉 code 文件 printf scan 表示 直接 只需要

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 }

題解【2.23考試T3】val