题意:给出n, 求出把n写成若干个正整数的立方和的方案数,
例如21有3种写法, 21=1^3+1^3+...+1^3 = 2^3+1^3+....+1^3 = 2^3+2^3+1^3+...+1^3;
99有440 022 018 293种写法
思路:
一开始比较容易想到的是 dp[i][j]表示用不超过i的整数 的立方和 累加和为J的方案数;
以此为出发点,我们可以得到 dp[i+1][j+(i+1)^3]+=dp[i][j], 也就是在dp[i][j] 的每一个方案都加上一个 (i+1)^3 , 从而得到j+(i+1)^3 ,
当然dp[i][j] 还可以对 dp[i+1][j+2*(i+1)^3]有贡献,
还可以对 dp[i+1][j+3*(i+1)^3]有贡献,
还可以对 dp[i+1][j+4*(i+1)^3]有贡献,
.........
对 dp[i+1][j+k*(i+1)^3]有贡献。 (条件是j+k*(i+1)^3《10000)
那么我们就可以得到转移方程 三层循环
d[0][0] = 1;
for(int i = 1; i <= 22; i++) //22^3>10000
for(int j = 0; j <= 10000; j++)
for(int a = 0; j+a*i*i*i<=10000; a++)
d[i][j+a*i*i*i] += d[i-1][j];
但是这样显然超时啦。。需要优化
对其dp[ i ][ j ]分两种情况讨论,
情况1】 没有用上一个i
情况2】用了至少一个i
情况1显然就是 dp[i-1][j] ,一个i都没用而得到了J
对于情况2,我们可以看看dp[i][j]之前得到的答案有没有可以利用的。
可以发现dp[i][j-i^3] 表示用不超过i的整数 的立方和 累加和为J-i^3的方案数, 那么他里面用了多少个i我们是不知道的,但是我们可以知道的是,只要给dp[i][j-i^3] 的任一个方案,再加上一个i^3,就可以得到 一个 J,并且我们至少用上了一个i(我们自己加的),
所以 dp[i][j-i^3]的方案数,其实恰恰是 【 情况2】, 用了至少一个i的的dp[i][j]
............至此dp[i][j]就是两种情况叠加起来了
PS:有个细节是, 情况2下,至少用上一个I的前提是 J>=i^3(不然也没法用)
得到的转移方程是:
for(i=1;i<=21;i++)
{
for(j=0;j<=10000;j++)
{
if (j>=i*i*i)
dp[i][j]=dp[i-1][j]+dp[i][j-i*i*i];
else
dp[i][j]=dp[i-1][j];
}
}
AC代码:o(n)
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
long long dp[30][10005];
int main()
{
dp[0][0]=1;
long long i,j,k;
for(i=1;i<=21;i++)
{
for(j=0;j<=10000;j++)
{
if (j>=i*i*i)
dp[i][j]=dp[i-1][j]+dp[i][j-i*i*i];
else
dp[i][j]=dp[i-1][j];
}
}
int n;
while(scanf("%d",&n)!=EOF)
printf("%lld\n",dp[21][n]);
return 0;
}
o(n^2)代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
long long dp[30][10005];
const long long MAX=500000000000;
int main()
{
dp[0][0]=1;
long long i,j,k;
for(i=1;i<=21;i++)
{
for(j=0;j<=10000;j++)
{
for (k=0;j+k*i*i*i<=10000;k++)
{
dp[i][j+k*i*i*i]+=dp[i-1][j];
}
}
}
int n;
while(scanf("%d",&n)!=EOF)
printf("%lld\n",dp[21][n]);
return 0;
}