回复 91楼 rolimi
你的代码仍然不能通过“6、3、3、2、2、2、2”包容量为10的测试。我现在的代码好像已经能达到最优了,小数据量的极端测试完全没问题,大数据量的随机系列测试已经大大优于以往(相同系列,随机算式是rand()%(m-2)+2),比如我随机的1000,1000也能达到511个包,原来需要535个包,算法的主要修改是:因为每个宝藏都必须装进袋子里,所以递归前并不需要最外层的循环,代码如下:
程序代码:#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);p++;
for(;n<s[p];p++); //调节源数据指针,减少递归次数
if(m>n)
{//临时集合里的组合最接近包容量则复制到优化的子集合c里
for(i=0;t[i];i++)c[i]=t[i];
c[i]=0;m=n;
}
if(!m)return 1; //如果恰好等于包容量直接返回
for(i=p;s[i];i++)
{
n=bag(s,c,t,i,w); //1为最优解,3直接剪枝,因为改组组合使用完了源数据集合后还没有c里的组合优化
if(n==1)return n;
if(n==2)t[l+1]=0; //剪枝,取下一个数试
}
return 2; //如果是循环完毕则返回到上一级通知剪枝,走下一条回路。
}
void main()
{
int i,j,k,l,m,n,w,bc,sc,sw,esw,sb,mw,*p,*c,*t;
/*备忘:m包容量,n宝藏数量,bc包计数,sc宝藏计数(最终是否有遗漏依据)
sw宝藏总重,esw装包宝藏总重(检验装包算法)
sb理想用包数量,mw所需要的平均包容量。
小于20个宝藏的人为输入,否则电脑随机*/
while(1)
{
m=0;n=0;sw=0;esw=0;
printf("设置包容量(0退出):");
scanf("%d",&m);
if(m<1)break;
printf("宝藏数量(多于20个自动输入。0退出):",m);n=0;
scanf("%d",&n);
if(n<1)break;
p=(int*)malloc(sizeof(int)*(n+1));
c=(int*)malloc(sizeof(int)*(n+1));
t=(int*)malloc(sizeof(int)*(n+1)); //申请内存空间
if(n<20)
{
printf("输入%d个宝藏重量(用空格间隔):",n);
for(i=0;i<n;i++)scanf("%d",p+i);
}
else
for(i=0;i<n;i++)p[i]=rand()%m+1; //产生随机的源数据集合
p[n]=0;
for(i=0;i<n;i++)sw+=p[i]; //获取宝藏总重
sb=sw/m;
if(sw%m)sb++; //得到理想用包数量
mw=sw/sb;
if(sw%sb)mw++; //得到在理想包数量情况下平均每个包装包容量
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; //清除最优解集合数据
t[0]=0; //清除临时集合数据
if(p[0]>mw)mw=p[0]; //第一个包=宝藏比平均容量大则调整平均容量
l=bag(p,c,t,0,mw); //此处直接递归,去掉原外层循环
w=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",w); //显示最优子集单个包装入容量
esw+=w;
if(w)bc++; //包的个数递增
if((sb-bc)>0)
{
mw=(sw-esw)/(sb-bc);
if((sw-esw)%(sb-bc))mw++; //调整平均包容量
if(mw>m)mw=m;
}
else
mw=m;
}
printf("实际总重%d,装袋总重%d,理想装包数量%d,实际装%d个宝藏用去%d个包\n\n",sw,esw,sb,sc,bc);
free(p);free(c);free(t); //释放内存
}
}最近在努力学习动态规划,比较惭愧的是,还没理解透。汗~~~~~~~~~~~
[ 本帖最后由 wmf2014 于 2015-6-17 15:43 编辑 ]

能编个毛线衣吗?




