1. 程式人生 > 實用技巧 >DP搬運工1

DP搬運工1

D. DP搬運工1

記憶體限制:512 MiB 時間限制:1000 ms 輸入檔案:D.in 輸出檔案:D.out 題目型別:傳統 評測方式:文字比較

題目描述

給你n,k,求有多少個1到n的排列,滿足相鄰兩個數的max的和不超過。

輸入格式

一行兩個整數n,k。

輸出格式

一行一個整數ans表示答案mod 998244353。

樣例

樣例輸入1

4 10

樣例輸出1

16

樣例輸入2

10 66

樣例輸出2

1983744

資料範圍與提示

有50個測試點,第i個測試點為n=i,k<=$n^2$。

思路:

題外話:一般來說,會先設f[i][j]表示前i個數max的和為j的方案數,但轉移時因為不知道第i+1個數放在哪,所以會有後效性,不可行。

題解:根據上面錯誤的思路,可以加以為則$f[i][j][k]$表示前i個數,在放入前$i$個數後還有$j$個位置,$max$和為k的方案數。

   當i+1要放在這些數之間時,若i+1旁邊能放兩個數,則貢獻為0;若i+1旁邊能放一個數,貢獻為i+1;若一個數也不能放,則貢獻為i+1+i+1;

   當i+1放兩邊時,同上

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 typedef long long ll;//注意強轉long long 
6 const int maxn=50+5,mod=998244353; 7 int f[maxn][maxn*maxn],g[maxn][maxn*maxn]; 8 int main(){ 9 int n,k; 10 scanf("%d%d",&n,&k); 11 f[0][0]=1;//初始化是針對i==1時 12 for(int i=2;i<=n;i++){//注意i應從2開始,因為初始化針對1 13 memcpy(g,f,sizeof(f));//g儲存i-1的資訊 14 memset(f,0,sizeof(f));
15 for(int j=0;j<=n-i+1;j++){//因為g儲存i-1的資訊,j<=n-(i-1) 16 for(int m=0;m<=k;m++){ 17 int x=g[j][m]; 18 if(!x) continue; 19 if(j)f[j][m+i]=(ll)(f[j][m+i]+(ll)2*x*j)%mod; 20 if(j)f[j+1][m]=(ll)(f[j+1][m]+(ll)x*j)%mod; 21 if(j)f[j-1][m+i+i]=(ll)(f[j-1][m+i+i]+(ll)x*j)%mod; 22 f[j][m+i]=(ll)(f[j][m+i]+(ll)2*x)%mod; 23 f[j+1][m]=(ll)(f[j+1][m]+(ll)2*x)%mod; 24 } 25 } 26 } 27 int ans=0; 28 for(int i=0;i<=k;i++) ans=(ans+f[0][i])%mod; 29 printf("%d\n",ans); 30 return 0; 31 }
View Code