装载问题(回溯法)
1、问题
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且∑wi <= c1 + c2。
问是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船。如果有,找出一种装载方案。
2、解析
c1和c2是相互独立的,即如何在c1或者c2上,放置物品是互不干扰的。
但是假如先在c1上放置货物,则因剩下的货物不同,会使c2上装载的货物的情况发生变化。
因此,为了保证c1上装载的情况,满足c1和c2将所有的货物都装下,c1上应该尽可能的多放物品。
3、设计
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int num=100; 4 int n,c1,c2,w[num];// n个集装箱,A,B货轮载重量分别为C1,C2,W[i],第i个集装箱的重量 5 int cw,bw,rw;//cw,当前集装箱货物重量;bw,最优载重重量,rw,剩余集装箱重量; 6 int x[num],bx[num];//x[],A货轮的当前结果;bx[],A货轮的最优结果; 7 void BackTrack(int i) { 8 //处理完了前n个集装箱; 9 if(i>n){ 10 if(cw>bw){//cw,目前A中装了cw重量的集装箱; 11 //更新最优解; 12 bw=cw; 13 for(int i=1;i<=n;i++) bx[i]=x[i]; 14 } 15 return; 16 } 17 //rw表示处理完第i个之后(选或不选),还剩下rw-w[i]重量的集装箱未处理; 18 rw-=w[i]; 19 if(cw+w[i]<=c1){//cw,第i个货箱之前的重量 + 第i个货箱小于A的最大重量C1; 20 cw+=w[i];//加上 21 x[i]=1;//标记i被选 22 BackTrack(i+1); 23 cw-=w[i];//减去重量 24 x[i]=0;//撤销标记; 25 } 26 //不选择第i个物品的话; 27 //if cw:表示[1:i)的数据 rw:表示(i,n]的数据 ,不包括第i个的数据 28 //如果不包括第i的数据的和(cw+rw) 大于 目前最优解bw,则可以递归下去; 29 if(cw+rw > bw){ 30 x[i]=0; 31 BackTrack(i+1); 32 } 33 34 //处理完第i个物品当前的情况了; 35 //因为再上一层,有两种情况; 36 //1;选择第i物品; 37 //2:不选择第i个物品 38 //如果目前处理的是上一层第1种情况,那么我们就有必要加上这个w[i]; 39 //否则会影响上一层处理第2种情况; 40 rw+=w[i]; 41 return ; 42 } 43 int main(){ 44 scanf("%d%d%d",&n,&c1,&c2); 45 for(int i=1;i<=n;i++) { 46 scanf("%d",&w[i]); 47 rw+=w[i];//rw表示目前最优集装箱的剩余重量; 48 } 49 //递归回溯 50 BackTrack(1); 51 //bw表示A货轮装下的货物重量;剩余的重量 > B可以放下的最多,则不可; 52 if(rw-bw>c2){ 53 printf("没有装载方案\n"); 54 }else{ 55 printf("货轮A:\n"); 56 for(int i=1;i<=n;i++) { 57 if(bx[i]) { 58 printf("%d ",i); 59 } 60 } 61 printf("\n货轮B:\n"); 62 for(int i=1;i<=n;i++) { 63 if(0==bx[i]) { 64 printf("%d ",i); 65 } 66 } 67 } 68 return 0; 69 }
4、分析
最坏情况要遍历图中所有结点,算法的时间复杂度为O(2")。因为,叶子结点有2",每个结点要计算机装载量以判断是否回溯。
5、源码
https://github.com/ChenyuWu0705/Algorithm-Analyze-and-Design/blob/main/loan.cpp