题目:
x[1]+x[2]+x[3]+…+x[n]=n, 这里
0 <= x[i] <= n && 1 <= i <= n
x[i] <= x[i+1] <= x[i]+1 && 1 <= i <= n-1
对于一个给定的n,Gorwin想要知道有多少xi的组合满足上述等式。由于结果比较大,输出答案对m取余的结果就行。
思路:1.观察发现数字种类只可能有srqt(n)种
2.发现第一个数只能从0或者1开始,想试试用dp来做这个题。
3.dp[n][m]代表当前x1,x2....xi中最大值为m(m <= sqrt(n)),sum(xi)为n(n <= 50000)的值存在多少种
情况,状态转移方程也可以顺势写出,dp[n][m] = dp[n - m][m] + dp[n - m][m - 1](只是一个帮助思考的递推方程,
并不是最终的递推方程)。
4.但是发现没法了解究竟i是多大?!。。也就是没法记录已经存在多少个xi了
5.思考后发现是不是可以不去管已经存在多少个xi了,因为如果假设1开始,i最大不会超过n,所以可以不必理
会存在多少个xi了,如果xi总数少于n直接用前缀0补齐就行了
6.可以观察转移方程再优化节约一维空间
总结:最近考虑了很多问题发现,应该规范思考问题的方式,比如
1.首先要明确要思考的的目标
2.其次先从最简单的情况开始考虑(比如从枚举开始考虑,不要轻易为题目定性),争取做到不遗漏
3.将步骤写在纸上就可以避免重复思考,并且可以发现思维漏洞
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define MAXM 50005
int dp[2][MAXM];
int main()
{
int _,n,m;
for(int kcas = scanf("%d",&_);kcas <= _;kcas++)
{
scanf("%d%d",&n,&m);
int l = sqrt(2.0 * n + 1);
memset(dp,0,sizeof(dp));
dp[1][1] = 1;
int ans = 0;
for(int j = 1;j <= l;j++)
{
for(int i = 1;i <= n && i + j <= n;i++)
{
dp[j & 1][i + j] = (dp[j & 1][i] + dp[j & 1][i + j]) % m;
dp[(j + 1) & 1][i + j + 1] = (dp[j & 1][i] + dp[(j + 1) & 1][i + j + 1]) % m;
}
ans = (ans + dp[j & 1][n]) % m;
memset(dp[j & 1],0,sizeof(dp[j & 1]));
}
printf("Case #%d: %d\n",kcas,ans);
}
}