这个问题和整数拆解问题是一样的;就是将n拆成不大于m的数的和;
例如: n=6,m=6;
6=6;
6=5+1;
6=4+2; 6=4+1+1;
6=3+3; 6=3+2+1; 6=3+1+1+1;
6=2+2+2; 6=2+2+1+1; 6=2+1+1+1+1;
6=1+1+1+1+1+1;
按如下分析:分解函数Split(n,m)
(1) m > n
在整数划分中实际上最大加数不能大于n,因此在这种情况可以等价为split(n, n);
可用程序表示为if(m > n) return split(n, n);
(2) m = n
这种情况可用递归表示为split(n, m - 1) + 1,从以上例子中可以看出,就是最大加数为6和小于6的划分之和
用程序表示为if(m == n) return (split(n, m - 1) + 1);
(3) m < n
这是最一般的情况,在划分的大多数时都是这种情况。
从上例可以看出,设m = 4,那split(6, 4)的值是最大加数小于4划分数和整数2的划分数的和。因此,split(n, m)可表示为split(n, m - 1) + split(n - m, m)
代码:
#include<iostream>
using namespace std;
int Split(int n,int m) //递归解法
{
if(m==1||n==1) return 1;
if(m>n) return Split(n,n);
else if(m==n) return Split(n,m-1)+1;
else return Split(n-m,m)+Split(n,m-1);
}
int dp[121][121]; //(121,121)大约2*10^9种
void DP() //动态规划解法
{
int i,j;
memset(dp,0,sizeof(dp));
for( i=1;i<=12;i++)
for( j=1;j<=12;j++){
if(i==1||j==1) dp[i][j]=1;
else if(j>i) dp[i][j]=dp[i][i];
else if(i==j) dp[i][j]=dp[i][j-1]+1;
else dp[i][j]=dp[i-j][j]+dp[i][j-1];
}
}
int main()
{
int T,n,m;
scanf("%d",&T);
DP();
while( T--){
scanf("%d%d",&n,&m);
printf("%d\n",dp[n][m]);
}
return 0;
}