当问题是方案数时,一般都是动态规划问题。
基于递归分治的思想,如何将n的划分,下降为小于n的划分。需要注意到每一种划分中有两个数字比较重要,最小值和最大值。思考如何递归时必然从这两个值中某一个入手。
解题思路:从最大值入手。设定dp(i,j)为整数i且最大值为的划分方案数。那么可以递归向下得到dp(i,j)的方案数是:
动规方程为:
因为dp(i,j)定义的是i最大值一定是2^j的方案数,所以要求整数n的全部划分方案数时,其结果为dp(n,1)+dp(n,2)+dp(n,4)+...........的总和。
题目不能使用递归,复杂度太高,采用递推的方法,用二维数组表示dp[i][j],按从小到大次序顺序推出所有解。算法复杂度为。
下面代码中lg数组用于求2的对数值,1<<j属于移位操作,将1左移j位得到的结果是。
(友情提示:本代码已开启防抄袭模式,请勿复制粘贴)
#include <iostream>//ASI
typedef long long ll;
using namespace std;
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,k,n,dp[10005][21]= {0},lg[100005],sum=0;
cin>>n;
lg[0]=-1;/**<一种以2为底对数处理技巧*/
for(i=1; i<=n; i++)/**< 可以快速通过数组lg[i]得到以2为底i的对数 */
lg[i]=lg[i/2]+1;
for(i=1; i<=n; i++)
{
for(j=0; j<=lg[i]; j++) /**< dp[i][j]表示i的一个加法序列,且序列中最大值为2的j次幂 */
{
if(1<<j==i) /**< 特殊处理,当i就是2的整数次幂时,其自身也是一种方案。如8=8 */
dp[i][j]=1;
for(k=0; k<=j; k++) /**<dp[i][j] 最大值是2的j幂,那么拿走最大值,剩下i-(1<<j),其最大值只要小于2的j幂即可 */
dp[i][j]=(dp[i][j]+dp[i-(1<<j)][k])%100000000;
}
}
for(i=0;i<=lg[n];i++) /**< n最大值为1,2,4,8....lg[n],全部的方案数总和即为答案 */
sum =(sum+dp[n][i])%100000000;
cout<<sum;
return 0;
}