回复 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 编辑 ]
能编个毛线衣吗?