关于N个鸡蛋放在M个篮子里等系列问题详解

N > M。求出满足如下要求的所有鸡蛋方法。要求:1.篮子不能为空;2.对于任意正整数n<=N, 能取x个篮子,使篮子里的鸡蛋数总和等于n。

 

实现方法一:

#include <cstring>
#include <iostream>

//namespace al{
int min(int x, int y)
{
	return x<=y?x:y;
}
void egg (int n, int m, int *&a,int size)
{ // n是鸡蛋数,m是篮子数,a数组用来存储结果,size是a数组的大小

	for (int i=min((n+1)/2, a[m]); i>=1; --i)
	{
		if (n<m) 
		{
			continue;
		}
		else if (n == m)
		{
			for (int j=m-1; j>=0; --j)
			{
				a[j] = 1;
			}
			m = 1;
			n = 0;
		}
		if (m == 1) 
		{
			if (n > 1) 
			{
				continue;
			}
			else
			{
				for (int k=0; k<size; ++k)
					std::cout<<a[k]<<" ";
				std::cout<<std::endl;
				return ;
			}
		} 
		else 
		{
			a[m-1] = i;
			egg (n-i, m-1, a, size);
		}
	}
}
//} //namespace al

int main()
{
	int n=20;
	int m=10;
	int* a = new int[m];
	a[0] = 1;
	int *&aa = a;
	for (int i=(n+1)/2; i>=1; --i)
	{ //由于egg函数中有i=min((n+1)/2, a[m])这句,所以数组最后一位是a[m-1],不存在a[m],所以这里初始处理下
		memset(a, 0, m);
		a[m-1] = i;
		//al::
		egg (n-i, m-1, aa, m);
	}
}

 

实现方法二:

#include <iostream>   
using namespace std;   
long pow2[20];   
int N,M;   
int ans[1000];   
void solve( int n , int m , int Min )   
{   
    if(n == N && m == M)   
    {   
      for(int i=0;i<M;i++)   
      {   
        cout<<ans[i]<<" ";         
      }    
      cout<<endl;   
      return ;         
    }    
    else if( n + (M-m)*Min > N || N > pow2[M-m]*n + pow2[M-m]-1)   
    return ;   
    else  
    {   
       for(int i = Min; i <= n+1; i++)   
       {   
          ans[m] =  i;       
          solve(n+i,m+1,i);    
       }                  
    }             
}     
int main()   
{   
  pow2[0] = 1;   
  for(int i=1;i<20;i++)   
  {   
    pow2[i] = pow2[i-1]<<1;          
  }   
    cin>>N>>M;   
    if( M > N || pow2[M]-1 < N)   
    {   
      cout<<"没有有效解"<<endl;               
    }          
    solve( 0 , 0 , 1 );   
    system("pause");       
    return 0;      
}  


说明:

1.n + (M-m)*Min > N 剪枝条件:放n个鸡蛋后,后面的篮子里即使都放Min个,总鸡蛋数都超过了N个。说明鸡蛋太少了
2.当前篮子放n个鸡蛋,下一个篮子放鸡蛋的个数为Min~n+1,也就是最多放n+1个,再下一个篮子最多放2n+2,4n+4...(n+1)*2^(M-m-1)
   当前篮子放n个,如果以后按最多的放,所有篮子的鸡蛋总和如果小于N,说明鸡蛋太多,放不完,要剪枝。即 
    n+(n+1)(2^0+2^1+2^2+2^3+...+2^(M-m-1))<N
    化简得:
    N > pow2[M-m]*n + pow2[M-m]-1

    此外main函数里的判断pow2[M]-1 < N也是按照这个思路推导的。

实现方法三:

#include <iostream>
using namespace std;

#define MAX_M   32
int ar[ MAX_M + 1 ];
int egg = 9 , box = 5;

void place_egg( int n , int m , int max )
{
    if( m == 1 )
    {
        ar[ 1 ] = n;
        for( int i = 1 ; i <= box ; i++ )
            cout << " " << ar[ i ];
        cout << endl;
        return;
    }

    if( m > n || n > ( 1 << m ) - 1 )
        return;

    if( ( n + 1 ) / 2 < max )
        max = ( n + 1 ) / 2;

    for( int i = max ; i >= ( n + m - 1 ) / m ; i-- )
    {
        ar[ m ] = i;
        place_egg( n - i , m - 1 , i );
    }
}

int main()
{
    place_egg( egg , box , egg );
    return 0;
}


实现方法四:

/** 
 * 假设 n>m 并且 n小于100 
 * @author Jason 
 * 2011.3.30 
 */  
public class Test {  
    private int m;  
    private int n;  
    private int eggs[];  
    private int numAnswer;  
      
    Test(){  
        m=10;  
        n=20;  
        numAnswer=0;  
        eggs =  new int[m];  
        for(int i=0;i<m;i++){  
            eggs[i]=0;  
        }  
    }  
    private void fill(boolean [] state, int step, int sum){  
        if(step>=m){  
            state[sum] = true;  
            return ;  
        }  
        fill(state,step+1,sum);  
        fill(state,step+1,sum+eggs[step]);  
    }  
      
    /** 
     * 判断是否满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到 
     * 算法:暴力枚举所有篮子的组合 
     * @return 
     */  
    private boolean judge(){  
        boolean [] state = new boolean [n+1];  
        for(int i=0;i<=n;i++){  
            state[i] = false;  
        }  
          
        fill(state,0,0);  
          
        for(int i=1;i<=n;i++){  
            if(!state[i]){  
                return false;  
            }  
        }  
        return true;  
    }  
      
    /** 
     * 给每个篮子分鸡蛋,升序(后一个篮子的鸡蛋必须不小于前一个篮子,避免重复计算) 
     * @param pre 前一个篮子鸡蛋数 
     * @param already 前step个篮子 已使用的鸡蛋数 
     * @param step 第step个篮子 
     */  
    public void solve(int pre,int already, int step){  
        if(step==m-1){  
            //最后一个篮子   
            eggs[m-1]=n-already;  
            //不符合条件   
            if(eggs[m-1]<pre)    return;  
              
            //判断是否满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到   
            if(judge()) {  
                for(int i=0;i<m;i++){  
                    System.out.print(eggs[i]+" ");  
                }  
                System.out.println();  
                numAnswer++;  
            }  
            return ;  
        }  
          
        // 给第step个篮子装鸡蛋,pre 到 n-already 种可能   
        for(int i=pre; i<=n-already; i++){  
            eggs[step]=i;  
            //递归   
            solve(i,already+i,step+1);  
        }  
    }  
    public static void main(String arg []  ){  
        Test test = new Test();  
        test.solve(1,0,0);  
        System.out.println("可能情况的数量:"+test.numAnswer);  
    }  
}  



 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值