您的位置:首页 > 其它

UVA - 11137-Ingenuous Cubrency-DP+数学

2015-10-28 21:46 363 查看
题意:给出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]
);

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]
);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: