回复 21楼 BlueGuy
一天?你真解不出来。这么着吧,一个礼拜你能解出来,我送你一千专家分。就上面的各位中,只有rjsp知道了问题所在,但好像还没有适合的解决方案。其他人都是在猜而已,要么想法不对,要么是有生之年都得不到结果的办法。

重剑无锋,大巧不工
程序代码:#include <stdio.h>
#include <stdlib.h>
int sumarry(int *t)
{//统计数组中数据和
int i,s=0;
for(i=0;t[i];i++)s+=t[i];
return s;
}
int lenarry(int *t)
{//得到数组长度
int i;
for(i=0;t[i];i++);
return i;
}
void bag(int *s,int *c,int *t,int p,int w)
{//递归获取最接近袋子容量的组合,s数据源,c存放最接近包容量的数据,t递归得到的所有组合,w包容量
int i,j,k,l,m,n;
k=sumarry(t);l=lenarry(t);
if((k+s[p])<=w){t[l]=s[p];t[l+1]=0;}
for(i=p+1;s[i];i++){bag(s,c,t,i,w);t[l]=0;}
k=w-sumarry(c);l=lenarry(c);m=w-sumarry(t);n=lenarry(t);
if(k>m||(k==m&&l<n))
{//如果递归的数据容量比c里面的数据更接近包容量则交换数据(相等则取数据项目多的)
for(j=0;t[j];j++)c[j]=t[j];
c[j]=0;
}
}
void main()
{
int i,j,n,m,bc,*p,*c,*t;
while(1)
{
printf("输入宝藏数量:");n=0;
scanf("%d",&n);
if(n<=0)break;
p=(int*)malloc(sizeof(int)*(n+1));
c=(int*)malloc(sizeof(int)*(n+1));
t=(int*)malloc(sizeof(int)*(n+1));
printf("输入宝藏:");
for(i=0;i<n;i++)
{
scanf("%d",p+i);
c[i]=t[i]=0;
}
p[n]=0;
printf("输入袋子袋子容量:");
scanf("%d",&m);
bc=0;//袋子数量清零
while(p[0])
{
for(i=0;i<=n;i++)c[i]=0;//初始化最接近包容量的数据
for(i=0;p[i];i++)
{//开始递归组合,得到c数据
for(j=0;j<=n;j++)t[j]=0;
bag(p,c,t,i,m);
}
for(i=0;c[i];i++)
{
for(j=0;p[j]!=c[i]&&p[j];j++);
p[j]=-1; //对c里面出现的数据在p里面做标记,好分离子集合
printf("%d,",c[i]);
}
printf("\n");
for(i=0,j=0;p[i];i++)
{
if(p[i]>0)
{
p[j]=p[i];
j++; //将子集合c从源集合p里分离,直到源集合没有数据,结束。
}
}
p[j]=0;bc++;
}
printf("需要%d个包\n",bc);
free(p);
free(c);
free(t);
}
}
程序代码:#include <stdio.h>
#include <stdlib.h>
int sumarry(int *t)
{//统计数组中数据和
int i,s=0;
for(i=0;t[i];i++)s+=t[i];
return s;
}
int lenarry(int *t)
{//得到数组长度
int i;
for(i=0;t[i];i++);
return i;
}
int bag(int *s,int *c,int *t,int p,int w)
{//递归获取最接近袋子容量的组合,s数据源,c存放最接近包容量的数据,t递归得到的所有组合,w包容量
int i,k,l,m,n;
k=sumarry(t);l=lenarry(t);
if((k+s[p])>w)return 0; //返回0,是发现当前数据和大于包容量,返回上一级循环跳过该数据
t[l]=s[p];t[l+1]=0;
m=w-sumarry(c);n=w-sumarry(t);
if(m>n)
{//临时集合里的组合最接近包容量则复制到优化的子集合c里
for(i=0;t[i];i++)c[i]=t[i];
c[i]=0;m=n;
}
if(!m)return 1; //如果恰好等于包容量直接返回
for(p++;n<s[p];p++); //调节源数据指针,减少递归次数
if(!s[p]&&sumarry(t)!=sumarry(c))return 3; //如果数据指针处没有数据则直接返回到主函数的调用者
for(i=p;s[i];i++)
{
n=bag(s,c,t,i,w); //1为最优解,3直接剪枝,因为改组组合使用完了源数据集合后还没有c里的组合优化
if(n==1||n==3)return n;
if(n==2)t[l+1]=0; //剪枝,取下一个数试
}
return 2; //如果是循环完毕则返回到上一级通知剪枝,走下一条回路。
}
void main()
{
int i,j,k,l,m,n,bc,sc,*p,*c,*t;
while(1)
{
m=45;
printf("袋子容量为%d,输入宝藏数量(0退出):",m);n=0;
scanf("%d",&n);
if(n<=0)break;
p=(int*)malloc(sizeof(int)*(n+1));
c=(int*)malloc(sizeof(int)*(n+1));
t=(int*)malloc(sizeof(int)*(n+1)); //申请内存空间
for(i=0;i<n;i++)p[i]=rand()%(m-20)+2; //产生随机的源数据集合
p[n]=0;
for(i=0;p[i+1];i++)
{
for(j=i+1;p[j];j++)
{
if(p[j]>p[i])
{
p[i]^=p[j];
p[j]^=p[i];
p[i]^=p[j];
}
}
}//冒泡从大到小排序
for(i=0;p[i];i++)printf("%d,",p[i]); //输出排序后的源数据
printf("\n--------------------------------\n");
bc=0;sc=0; //袋子数量清零,待装包的宝藏数量清零
while(p[0])
{
c[0]=0; //清除最优解集合数据
for(i=0;p[i];i++)
{//开始递归组合,得到c数据
t[0]=0; //清除临时集合数据
l=bag(p,c,t,i,m);
if(l==1)break; //已经得到一组最优解,跳出循环输出最优解
}
m=sumarry(c); //调整包容量,可加快执行速度,也可注释掉,但执行速度会慢些
for(i=0;c[i];i++,sc++)
{
l=c[i];
for(j=0,k=0;p[j];j++)
{
if(p[j]!=l)
{
p[k]=p[j];
k++;
}
else
l=-1;
}
p[k]=0;
printf("%d,",c[i]); //显示最优子集,从源集合中分离子集
}
printf("---%d\n",sumarry(c)); //显示最优子集单个包装入容量
bc++; //包的个数递增
}
printf("装进%d个宝藏需要%d个包\n",sc,bc);
free(p);free(c);free(t); //释放内存
}
}
