UVA - 11137-Ingenuous Cubrency-DP+数学

题意:给出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;
 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值