回溯法—装载问题

问题描述:

    一共有n个货物要装上两艘重量分别为c1和c2的轮船上,其中货物i的重量为Wi,且:

                             

    要求确定是否有一个合理的装载方案可将货物装上这两艘轮船。


采取策略:      
 (1)首先将第一艘轮船尽可能装满;
 (2)将剩余的集装箱装上第二艘轮船。将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,
     使该子集中集装箱重量之和最接近。由此可知,装载问题等价于以下特殊的0-1背包问题:

                            

限界函数:

    定义一个CW表示到达当前结点O的重量,如果CW>C1,表示以O为根的子树不能产生一个可行的解,避免移动。

缺陷:

    一个可行结点的右孩子总是可行。

算法改进:

    如果当前结点的右子树不可能包含比当前最优解更好的解,就不移动到右子树上。

    设bestW为当前的最优解,Z为解空间树的第i层的一个结点。

改进的限界函数:

    设r为剩余货箱的重量,当CW+r<=bestW时,没有必要去搜索Z的右子树。


话不多说,直接上代码:

    

package set;
public class Loading {
	static int n;//货箱数目
	static int[] w;//货箱重量数组
	static int c;//第一艘船的重量
	static int cw;//当前装载的重量
	static int bestw;//目前最优装载的重量
	static int r;//剩余货箱的重量
	
	static int[] x;//当前解,记录从根至当前结点的路径
	static int[] bestx;//记录当前最优解
	
	
	public static int MaxLoading(int[] ww,int cc) {
		//初始化数据成员,数组下标从1开始
		n = ww.length - 1;
		w = ww;
		c = cc;
		cw = 0;
		bestw = 0;
		x = new int[n+1];
		bestx = new int[n+1];
		
		//初始化r,即剩余最大重量
		for(int i =1;i<=n;i++) {
			r += w[i];
		}
		
		//计算最优载重量
		backtrack(1);
		return bestw;
	}
	
	//回溯算法
	public static void backtrack(int t) {
		if(t>n) {//到达叶结点
			if(cw>bestw) {
				for(int i=1;i<=n;i++) {
					bestx[i] = x[i];
				}
				bestw = cw;
			}
			return;
		}
		
		r -= w[t];
		if(cw + w[t] <= c) {//搜索左子树
			x[t] = 1;
			cw += w[t];
			backtrack(t+1);
			cw -= w[t];//回溯
		}
		
		if(cw + r>bestw) {
			x[t] = 0;//搜索右子树
			backtrack(t+1);
		}
		
		r += w[t];//恢复现场
		
	}
	/*
	 * 如果当前节点的右子树不可能包含比当前最优解更好的解时,就不移动到右子树上!
	设bestw为当前最优解,Z为解空间树的第i 层的一个节点
		为剩余货箱的重量;当cw+r<=bestw时,没有必要去搜索Z 的右子树:
		当前载重量cw+剩余集装箱的重量r当前最优载重量bestw
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] ww = {0,20,30,60,40,40};
		int c1 = 100;
		int c2 = 100;
		int n = ww.length - 1;
		
		MaxLoading(ww,c1);
		int weight2 = 0;//保存第二艘船可能要装的重量
		for(int i=1;i<=n;i++) {
			weight2 += ww[i]*(1-bestx[i]);//bestx[i]的值只能为0或1
			
		}
		if(weight2>c2) {
			System.out.println("无解");
		}
		else {
			System.out.println("第一艘船装载货物的重量: " + bestw);
			System.out.println("第二艘船装载货物的重量: " + weight2);
			
			//第一艘船的装载情况
			for(int i = 1;i<=n;i++) {
				if(bestx[i] == 1) {
					System.out.println("第" + i + "件货物装入第一艘船");	
				}
			}
			
			//第二艘船的装载情况
			for(int i = 1;i<=n;i++) {
				if(bestx[i] == 0) {
					System.out.println("第" + i + "件货物装入第二艘船");
					
				}
			}
			
		}
		
	}

}

运行结果:

    



猜你喜欢

转载自blog.csdn.net/qq_38046510/article/details/80054969