问题:有N个蛋和M个篮子,把蛋放到M个篮子里,每个篮子都不能为空。另外,需要满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到。写出程序,使得输入一个(N,M),输出所有可能的分配情况。 #include<iostream> #include<math.h> #include<malloc.h> #include<fstream> using namespace std; struct solution { int *ptr; struct solution *next; }; typedef struct solution solu; int* first(int n,int m); //计算出第一种组合 solu* others(int n,int m,solu *head,solu *prior); //计算出其他组合 int sum(int n,int *p); //计算前n-1个篮子里的蛋数和 bool only(solu *head,int *p,int m); //检查组和是否满足要求 int ways; //全局变量,保存组合的方法数 void main() { int n=0,m=0,i=0,k=0; solu *head=NULL; solu *temp=NULL; LABLE: cout<<"输入鸡蛋数N="; cin>>n; cout<<"输入篮子数M="; cin>>m; if(m<=0||n<=0||m>n||(double)n>=pow(2.0,m)) //对m,n的约束 { cout<<"输入不合法!"<<endl; goto LABLE; } cout<<"正在计算..."<<endl; head=others(n,m,head,NULL); //调用others开始计算 temp=head; ofstream file("D://egg.txt"); //结果保存着这个目录下 cout<<"共有"<<ways<<"种组合方式:"<<endl; file<<"共有"<<ways<<"种组合方式:"<<endl; k=ways; while(temp!=NULL&&ways) { cout<<"方式"<<k-ways+1<<":"<<endl; file<<"方式"<<k-ways+1<<":"<<endl; for(i=0;i<m;i++) { cout<<*(temp->ptr+i)<<" "; file<<*(temp->ptr+i)<<" "; } delete[] temp->ptr; temp=temp->next; cout<<endl; file<<endl; ways--; } file.close(); cout<<"操作结果保存在D://egg.txt,您可以查看或删除之。"; cin>>i; } int sum(int n,int *p) //计算前n-1个篮子里的总蛋数 { int total=0; for(int i=0;i<n;i++) { total+=*(p+i); } return total; } int* first(int n,int m) { int *p,i=0,temp1=0,temp2=0; p=(int *)malloc(m*sizeof(int)); for(i=0;i<m;i++) //每个篮子里放一个蛋 { *(p+i)=1; } //下面的分配满足的条件: //“总能找到几个篮子,使里面鸡蛋的和等于任意一个小于n的正整数” //下面的if~else语句完成一种组合,升序排列,并使后面的篮里的蛋尽量多 if(n-m>m-1) //剩下的蛋数大于前面m-1个篮子里的蛋数和, { *(p+m-1)+=m-1; while(sum(m,p)<n) //还有蛋剩余 { temp1=n-sum(m,p); //剩蛋数 for(i=m;i>0;) { temp2=sum(i-1,p); //第i个篮子前面的所有篮子里的蛋数的总和 if(*(p+i-1)<=temp2) //第i个篮子里的蛋数小于等于前面篮子里蛋的总数,给这个篮里加蛋 //否则,见else { if(temp1<=temp2-*(p+i-1)+1) //剩下的蛋可以全部放到第i个篮里,完毕 {*(p+i-1)+=temp1;break;} else {*(p+i-1)+=temp2-*(p+i-1)+1;break;} //在第i个篮子放可能达到的最多蛋数 } else i--; //检测前面那个篮子 } } } else *(p+m-1)+=n-m; //剩下的蛋数小于等于前面m-1个篮子里的蛋数和, //把所有的蛋都放到最后一个篮里,完成一种组合。 return p; } solu* others(int n,int m,solu* head,solu *prior) { int i=0,j=0,k=0; if(head==NULL) //还没有任何组合 { solu *s=new(solu); s->ptr=first(n,m); //调用first()生成满足后面的值最大的升序序列 head=s; head->next=NULL; prior=head; ways=1; } for(j=m-1;j>0;j--) //两重循环,开始计算其他组合 //原理是从后面的篮子里取出鸡蛋放入前面的篮子中 { if(*(prior->ptr+j)==1) //后面的篮子里蛋数为1,跳出循环 break; for(i=j-1;i>0;i--) //一个个往前挨 { if(*(prior->ptr+j)-1>*(prior->ptr+i)) //后面的篮子减掉后不能比前面的少,保持升序排列 { int *p=(int *)malloc(m*sizeof(int)); for(k=0;k<m;k++) { (*(p+k))=(*(prior->ptr+k)); } (*(p+j))--;(*(p+i))++; if(only(head,p,m)) //检查是否满足条件,满足则将结果添加到链表中 { solu *stemp=new(solu); stemp->ptr=p; stemp->next=head->next;head->next=stemp; head=others(n,m,head,stemp); ways++; } else delete[] p; } else if(*(prior->ptr+j)-1==*(prior->ptr+i)) continue; else break; } } return head; } bool only(solu *head,int *p,int m) //判断条件是否符合 { solu *s=head; int flag=0,i=0; for(int k=0;k<m-1;k++) { if(*(p+k+1)<*(p+k)||*(p+k+1)>sum(k+1,p)+1) //两个条件:1、升序,2、后面的数必须小于等于前面的篮子总数和加1 return false; } while(s!=NULL) //判断是否有过相同的组合,有则返回false { flag=0; for(i=0;i<m;i++) { if(*(s->ptr+i)!=*(p+i)) { flag=1; break; } } if(!flag) { return false; } s=s->next; } return true; //检查通过,返回true }