苹果放在盘子里(动态规划问题)

  1. m个相同的苹果放在n个相同的盘子里,允许有空盘子
  2. m个相同的苹果放在n个相同的盘子里,不允许有空盘子
  3. m个相同的苹果放在n个不同的盘子里,允许有空盘子
  4. m个相同的苹果放在n个不同的盘子里,不允许有空盘子

第一种情况
设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
如果n>m,必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响;即  if(n>m) f(m,n) = f(m,m)  
当n<=m时,不同的放法可以分成两类:即有至少一个盘子空着或者所有盘子都有苹果,

前一种情况相当于f(m,n) = f(m,n-1); 
后一种情况可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n). 
而总的放苹果的放法数目等于两者的和,即f(m,n) =f(m,n-1)+f(m-n,n)。

边界条件为m=0或n=1时,只有一种放法。

#include<iostream>
using namespace std;

int fun(int m,int n){
    // 没有苹果 或者 只有一个盘子
	if(m==0||n==1)
		return 1;
		
	if(m<n)
		return fun(m,m);
		
	else
		//if((m-n)<=1||n==1)
		//return 1;
		return fun(m,n-1)+fun(m-n,n);	
}
int main(){
	int m,n,t;
	cin>>t;
	while(t--){
		cin>>m>>n;
		cout<<fun(m,n)<<endl;
	}
	return 0;
}

第二种情况
盒子不能为空,所以可以当成将m-n个球放到n个盒子里,盒子可以为空。

比如将10个球放到5个盒子里,可以当成5个球放到5个盒子里,盒子能为空。接着,再分情况,将球分为

(5,0,0,0,0)

(4,1,0,0,0)或(2,3,0,0,0)

(3,1,1,0,0)或(2,2,1,0,0)

(2,1,1,1,0)

(1,1,1,1,1)
也就是1个盒子不能为空,2个盒子不能为空,3个盒子不能为空,到5个盒子都不能为空。

用一个函数fun(m,n)来获取m个球放到n个盒子里不能为空的情况(也可以用能为空的,这里用的是不能为空的)

上面的可以转换成

fun(5,1)+fun(5,2)+fun(5,3)+fun(5,4)+fun(5,5)

#include <iostream>
using namespace std;
 
int fun(int m, int n) {
    if (m < n)   //m<n返回0
         return 0;
         
    if (n == 1 || (m - n) <= 1) //盒子为1时,球只有一个或0(0个即球刚好均分),时都只有一种分配方法
        return 1;
 
    int s = 0, count;   //s记录总数,count为递归次数
 
    if (m - n < n)       //去掉每个盒子分配的一个球后,球的数量少于盒子数,则只要递归球的个数次
        count = m - n;   //球数少的情况
    else
        count = n; //盒子数少的情况
 
    for (int i = 1; i <= count; i++) {
        s += fun(m - n, i); //递归
    }
 
    return s;
}
 
int main(int argc, char **argv) {
 
    cout << "\t";
    for (int i = 1; i <= 15; i++) {
        cout << i << "\t";
    }
    cout << endl;
 
    for (int i = 1; i <= 15; i++) {
        cout << i << "\t";
        for (int j = 1; j <= 15; j++) {
            cout << fun(i, j) << "    ";
        }
        cout << endl;
    }
}

第三种情况
思路和盘子不同是差不太多,只是多了一个组合数(在这里用Comd(n, m), 表示从n个中选出m个)。
开始分为两种可能:
当 m < n 时,m个苹果可以在n个盘子里选m个,也就是Comd(n, m)个,身下的(n-m)个盘子已经被考虑过了,而且不会影响接下来的摆放,直接去掉就行,所以方法数有 Comd(n, m) * f(m, m)

当 m >= n时,分为有盘子为空(有0)和盘子均不为空(无0):
有0时:从n个盘子中选出一个不放苹果,有Comd(n, 1)种选法,所以就是Comd(n, 1) * f(m, n-1)
无0时,从m个苹果中先拿出n个放满盘子,这n个苹果对后序摆放没有一点影响,直接去掉就行,然后就变成了(m-n)个苹果摆放在n个盘子问题 也就是 f(m-n, n)

递归出口(同盘子相同时一样)

int f(int m, int n)
{
	if (m==0 || n==1)
	{
		return 1;
	}
	else if (m < n)
	{
		return Comd(n, m) * f(m, m);
	}
	else if (m >= n)
	{
		return Comd(n, 1)*f(m, n-1) + f(m-n, n);
	}
}


第四种情况

#include <iostream>
using namespace std;
 
int fun(int m, int n) {
    if (m < n)   //m<n返回0
         return 0;
         
    if (n == 1 || m==n)
        return 1;
    if ((m - n) == 1) //盒子为1时,球只有一个或0(0个即球刚好均分),时都只有一种分配方法
      return  n;
    int s = 0, count;   //s记录总数,count为递归次数
 
    if (m - n < n)       //去掉每个盒子分配的一个球后,球的数量少于盒子数,则只要递归球的个数次
        count = m - n;   //球数少的情况
    else
        count = n; //盒子数少的情况
 
    for (int i = 1; i <= count; i++) {
        s += Comd(n, count)*fun(m - n, i); //递归
    }
 
    return s;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值