/*
Name: 666_放苹果
Copyright:
Author:
Date: 31-07-17 21:22
Description: 666_放苹果
查看 提交 统计 提问
总时间限制: 1000ms 内存限制: 65536kB
描述
把N个同样的苹果放在M个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据N和M,用一行输出相应的K。
样例输入
1
7 3
样例输出
8
算法思路:
回溯算法:有2种思路,一种是确保后面的盘子中苹果不比前面的少,另一种是确保后面的盘子中苹果不比前面的多。
第2种思路递归深度较少,但代码复杂些,特别要注意第n个盘子能放苹果的数量范围。
基本上递归问题都可以转化为记忆化搜索,然后转换为动态规划问题。
回溯算法相当于穷举,不但可以获得组合的数量,还可以存储具体的解空间。
记忆化搜索和动态规划算法均采用了确保后面的盘子中苹果不比前面的多的思路;其中动态规划进行了降维优化。
*/
#include<iostream>
using namespace std;
const int MAXN = 10; //苹果的最大数量
const int MAXM = 10; //盘子的最大个数
int A1[MAXN+1], A2[MAXN+1];
int M, N, s1, s2;
long long B[MAXM+1][MAXN+1] = {1}; //记录给定n个盘子装m个苹果的方案总数
long long B2[MAXM+1][MAXN+1] = {1}; //记录给定n个盘子装m个苹果的方案总数
long long pre[MAXN+1] = {1}; //记录给定n个盘子装m个苹果的方案总数
long long cur[MAXN+1] = {1}; //记录给定n个盘子装m个苹果的方案总数
long long F[MAXN+1] = {1}; //记录给定n个盘子装m个苹果的方案总数
void DFS_1(int k, int n); //n表示共n个苹果,k表示第k个盘子
void DFS_2(int k, int n); //n表示共n个苹果,k表示第k个盘子
long long Fun(int k, int n); //记忆化搜索
void Fun2(int k, int n); //动态规划:使用二维数组
long long Fun3(int k, int n); //动态规划:使用2个一维数组
long long Fun4(int k, int n); //动态规划:使用1个一维数组
int main()
{
int t;
Fun2(MAXM, MAXN);
cin >> t;
for (int k=0; k<t; k++)
{
cin >> N >> M;
s1 = 0;
DFS_1(1, N);
cout << s1 << endl;
s2 = 0;
int minNum = N/M + (N%M!=0);
for (A2[1]=N; A2[1]>=minNum; A2[1]--)//第一个盘中放A2[1]个苹果
{
DFS_2(2, N-A2[1]);
}
cout << s2 << endl;
cout << Fun(M, N) << endl;
cout << B2[M][N] << endl;
cout << Fun3(M, N) << endl;
cout << Fun4(M, N) << endl;
}
return 0;
}
void DFS_1(int k, int n) //n表示共n个苹果,k表示第k个盘子
{
if (k == M)//最后一个盘子
{
A1[k] = n;
cout << s1 << " : ";
for (int i=1; i<=k; i++)
cout << A1[i] << " ";
cout << endl;
s1++;
}
else //至少还有2个盘子
{
for (A1[k]=A1[k-1]; A1[k]+A1[k]<=n; A1[k]++)
{ //确保剩下的苹果不比当前盘子中的少
DFS_1(k+1, n-A1[k]);
}
}
}
void DFS_2(int k, int n) //n表示共n个苹果,k表示第k个盘子
{
if (n == 0 || k == M) //没有苹果或者没有盘子了
{
A2[k] = n;
cout << s2 << " : ";
for (int i=1; i<=k; i++)
cout << A2[i] << " ";
for (int i=k+1; i<=M; i++) //后面的盘中为空
cout << 0 << " ";
cout << endl;
s2++;
}
else
{
int maxNum = min(n, A2[k-1]);//确保后面的盘子中苹果不比前面的多
int minNum = n/(M-k+1) + (n%(M-k+1)!=0);
for (A2[k]=maxNum; A2[k]>=minNum; A2[k]--)
{
DFS_2(k+1, n-A2[k]);
}
}
}
long long Fun(int k, int n) //记忆化搜索
{
if (B[k][n] != 0)
return B[k][n];
if (k == 1 || n == 0)
B[k][n] = 1;
else if (n < k)
B[k][n] = Fun(n, n);
else
B[k][n] = Fun(k-1, n) + Fun(k, n-k);
return B[k][n];
}
void Fun2(int k, int n) //动态规划:使用二维数组
{
for (int i=1; i<=k; i++)//0个苹果放到i个盘子里
B2[i][0] = 1;
for (int j=1; j<=n; j++)//j个苹果放到1个盘子里
B2[1][j] = 1;
for (int i=2; i<=k; i++)
{
for (int j=1; j<i; j++)
{
B2[i][j] = B2[j][j];
}
for (int j=i; j<=n; j++)
{
B2[i][j] = B2[i-1][j] + B2[i][j-i];
}
}
}
long long Fun3(int k, int n) //动态规划:使用2个一维数组
{
for (int j=1; j<=n; j++)//j个苹果放到1个盘子里
pre[j] = 1;
for (int i=2; i<=k; i++)
{
for (int j=1; j<i; j++)
{
cur[j] = pre[j];
}
for (int j=i; j<=n; j++)
{
cur[j] = pre[j] + cur[j-i];
}
for (int j=1; j<=n; j++)
{
pre[j] = cur[j];
}
}
return pre[n];
}
long long Fun4(int k, int n) //动态规划:使用1个一维数组
{
for (int j=1; j<=n; j++)//j个苹果放到1个盘子里
F[j] = 1;
for (int i=2; i<=k; i++)
{
for (int j=i; j<=n; j++)
{
F[j] += F[j-i];
}
}
return F[n];
}