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