递推关系的运用加简单DP【UVA11137Ingenuous Cubrency】-------2015年1月27日
一:题意描述
本题就是求立方数之和。输入正整数n,求将n写成若干个正整数的立方和有多少种方法?
二:问题分析
本题主要的难点就是确定状态。我们可以建立多段图。节点(i,j)表示“使用不超过i的整数的立方,累加和为j“这个状态。设d(i,j)表示为从(0,0)到(i,j)的路径条数,最终答案
是d(21,n)。对于这个图而言,每一个节点(i,j)都只能从当前节点(i,j)到达节点(i+1,a*(j+1)^3+j)(a为j+a*(j+1)^3<maxn的常数)所以我们可以写出如下代码:
#include<iostream> #include<string.h> #include<math.h> #include<algorithm> using namespace std; #define MAXI 21 #define MAXN 10000+5 long long d[MAXI+1][MAXN]; int main() { int n; memset(d,0,sizeof(d)); d[0][0]=1; for(int i=1;i<=MAXI;i++) { for(int j=0;j<=MAXN;j++) { for(int a=0;j+a*i*i*i<=MAXN;a++) d[i][j+a*i*i*i]+=d[i-1][j]; } } while(cin>>n) { cout<<d[21][n]<<endl; } }
但是我们可以分析得出这个代码的时间复杂度接近O(N^3).所以我们应该把这个问题加以改进:
对于第i个数字而言,我们可以这样分析:如果我们取一次第i个数字得到d(i,j)那么可以知道这是从状态d(i,j-i^3)转化得到;当我们不取第i个数字得到d(i,j)那么可以知道这是从状态d(i-1,j)转化得到。那么我们便可以得到递推公式:d(i,j)=d(i-1,j)+d(i,j-i^3)(如果大家想说我们如果需要用多个i^3怎么办?是不是这个公式不在成立呢?仔细想想还是成立的,因为我们如果再往下递推一层或者多层我们便可以使用多次i^3,那么我们便可以把代码写成如下形式:时间复杂度仅仅是O(n^2),而且我们可以用滚动数组节约存储空间。
下面给出代码:
#include<iostream> #include<string.h> #include<math.h> #include<algorithm> using namespace std; #define MAXN 22*22*22 long long dp[MAXN]; int main() { int n; memset(dp,0,sizeof(dp)); long long c[22]; for(int i=1;i<22;i++) c[i]=i*i*i; dp[0]=1; for(int i=1;i<22;i++) for(int j=c[i];j<MAXN;j++) dp[j]+=dp[j-c[i]]; while(cin>>n) { cout<<dp[n]<<endl; } }
在合理选取递推式和状态时往往可以节省很多时间,提高效率。