(动态规划)A - 放苹果
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
Input
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
Output
对输入的每组数据M和N,用一行输出相应的K。
Sample Input
1
7 3
Sample Output
8
*这道题专门问了下数学老师,老师说这道题并没有什么通解,只能一个一个来分着数,因此我想了很久,看了博客中的一种思路,自愧不如,这种思路非常的成熟与有效,如下
解析:
设f(m,n)为m个苹果,n个盘子的放法数目,则先对n作讨论,
* 当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m)f(m,n)=f(m,m)
* 当n<=m:不同的放法可以分成两类:
(a)有至少一个盘子空着,即相当于f(m,n)=f(m,n-1);
(b)所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n)=f(m-n,n).而总的放苹果的放法数目等于两者的和,即f(m,n)=f(m,n-1)+f(m-n,n)递归出口条件说明:当n=1时,所有苹果都必须放在一个盘子里,所以返回1;当没有苹果可放时,定义为1种放法;递归的两条路,第一条n会逐渐减少,终会到达出口n1;第二条m会逐渐减少,因为n>m时,我们会returnf(m,m) 所以终会到达出口m0.
综上递推公式为:
代码展示
#include<bits/stdc++.h>
using namespace std;
int a[12][12];
int main()
{
int n,m,t;
cin>>t;
while(t>0)
{
--t;
cin>>m>>n;
a[0][0]=1;
for(int i=0;i<=m;++i )
for(int j=1;j<=n;++j)
{
if(i<j) a[i][j]=a[i][i];
else a[i][j]=a[i][j-1]+a[i-j][j];
}
cout<<a[m][n]<<endl;
}
return 0;
}
*然后看了他的题解之后顿时感觉有些简单了,但是,我自己也有一种不成熟的思路,就是将每个盘子中能呈苹果的数量进行评估,递归求解,即将每种可能都给列出来,正是数学老师所提到的,但是总是和答案有所差别,经过无数次调试,发现了多个错误后,写出如下AC代码:
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m;
int du(int i,int n1,int a)//i代表此盘子中能呈的最小数量,即前一个盘子中苹果
{ //的数量,n1代表现在还剩下的苹果数,a代表剩下的盘 //子数
if(a==2)//当盘子数只剩下2个,因为苹果数也确定,故方案数确定
{
int t=n1/a-i+1;//方案数即盘子(最大苹果数-最小苹果数+1)
if(t>0)
return t; //这里错过,没有判断是不是负数的情况
else return 0;
}
else
{
int ans=0;
for(int j=i;j<=n1/a;j++)//盘子所能呈的最大数量即剩下的苹果
{ //数除以盘子数
ans+= du(j,n1-j,a-1);//将此盘子放j个苹果后,判断放之后
} //剩下盘子的方案数
return ans;
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
if(n==1||m==1){cout<<1<<'\n';continue;}//一个盘子或苹果不用考虑多种 //情况
int ans=du(0,n,m);
cout<<ans<<'\n';
}
}
*无疑,相比起来,还是上一种的思路更加清晰与快捷,更加接近问题本质,但是我所想的方法得以实现也是一种很有成就感的事情,同时满足了自己的求知欲,一个问题可以有多种解法,这也正是算法的魅力。