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

 

posted @ 2020-08-17 20:54  ddoodd  阅读(305)  评论(0编辑  收藏  举报