算法设计与分析: 6-16 子集空间树问题(优先队列)

6-16 子集空间树问题(优先队列)


问题描述

试设计一个用优先队列式分支限界法搜索子集空间树的函数。该函数的参数包括结点可行性判定函数和上界函数等必要的函数,并将此函数用于解 0-1 背包问题。
0-1 背包问题描述如下:给定 n 种物品和一背包。物品 i 的重量是 wi w i ,其价值为 vi v i ,背包的容量为 C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?在选择 装入背包的物品时,对每种物品 i 只有 2 种选择,即装入背包或不装入背包。不能将物品 i 装入背包多次,也不能只装入部分的物品 i。
问题的形式化描述是,给定 C>0 C > 0 wi>0 w i > 0 vi>0 v i > 0 1in 1 ≤ i ≤ n ,要求找出 n 元 0-1 向量 (x1x2...xn) ( x 1 , x 2 , . . . , x n ) xi{01} x i ∈ { 0 , 1 } 1in 1 ≤ i ≤ n ,使得 i=1nwixiC ∑ i = 1 n w i x i ≤ C ,而且 i=1nvixi ∑ i = 1 n v i x i 达到最大。因此,0-1 背包问题是一个特殊的整数规划问题。

maxi=1nvixi m a x ∑ i = 1 n v i x i

maxi=1nwixiCxi{0,1},1in { m a x ∑ i = 1 n w i x i ≤ C x i ∈ { 0 , 1 } , 1 ≤ i ≤ n

数据输入:
第 1 行有 2 个正整数 n 和 C,分别表示有 n 种物品, 背包的容量为 C。接下来的 2 行中,每行有 n 个数,分别表示各物品的价值和重量。


Java

package Chapter6FenZhiXianJieFa;

import java.util.*;

public class ZiJiKongJianShuYouXianDuiLie {

    private static class HeapNode implements Comparable{
        int upprofit,profit;
        int weight;
        int level;
        int[] x;

        public int compareTo(Object o){
            HeapNode heapNode = (HeapNode) o;
            int result = Integer.compare(heapNode.upprofit, upprofit);

            return result;
        }
    }

    private static class Knap{
        int c,cw,wt;
        int cp,up,bestp;
        int[] w,p;
        int n;
        int[] bestx;

        private void newHeap(){
            H = new PriorityQueue<>(10000);
        }

        private void newHeapNode(){
            N = new HeapNode();
            N.x = new int[n+1];
        }

        private void init(){
            bestx = new int[n+1];
            for(int k=0; k<=n; k++) bestx[k]=0;
            cw=cp=0;
            bestp=0;
            up=Bd(1);
        }

        //计算结点所相应价值的上界
        private int Bd(int i){
            int cleft = c-cw;//剩余容量
            int b= cp;//价值上界
            //以物品单位重量价值递减序装填剩余容量
            while (i<=n && w[i]<=cleft){
                cleft -= w[i];
                b += p[i];
                i++;
            }
            //装填剩余容量装满背包
            if(i <= n) b+=p[i]*cleft/w[i];

            return b;
        }

        private boolean constraint(HeapNode N, int i){
            wt = cw+w[i];
            if(wt <= c){
                if(cp+p[i] > bestp) bestp=cp+p[i];
                return true;
            }

            return false;
        }

        private boolean bound(HeapNode N, int i){
            up = Bd(i+1);

            return up>=bestp;
        }

        //将一个新结点插入到最大堆H中
        private void addLiveNode(int i, int ch){
            N = new HeapNode();
            N.x = new int[n+1];
            for(int j=0; j<=n; j++) N.x[j]=bestx[j];
            N.upprofit = up;
            N.profit = cp;
            N.weight = cw;
            if(ch > 0) {N.weight=cw+w[i]; N.profit=cp+p[i];}
            N.level = i+1;
            N.x[i] = ch;
            H.add(N);
        }

        private boolean getNext(){
            if(H.isEmpty()) return false;
            N = H.poll();
            cw = N.weight;
            cp = N.profit;
            up = N.upprofit;
            i = N.level;
            for(int j=0; j<=n; j++) bestx[j]=N.x[j];
            if(i > n) return false;
            else return true;
        }

        //构造当前最优解
        private void solution(){
            for(int j=0; j<=n; j++) bestx[j]=N.x[j];
            while (true){//释放堆中所有结点
                if(H.isEmpty()) break;
                N = H.poll();
            }
        }

//        private void output(){
//            System.out.println(cp);
//            for(int j=1; j<=n; j++)
//                System.out.print(bestx[j]+" ");
//        }

        private void pqbb(){
            newHeap();
            newHeapNode();
            i = 1;
            init();
            //搜索子集空间树
            while (true){
                //检查左儿子结点
                if(constraint(N,i)) addLiveNode(i,1);
                //检查右儿子结点
                if(bound(N,i)) addLiveNode(i,0);
                //取下一扩展结点
                if(!getNext()) break;
            }
            solution();//构造最优解
//            output();
        }
    }

    private static int bestp;
    private static Queue<HeapNode> H;
    private static HeapNode N;
    private static int i;

    public static void main(String[] args){
        int n,c;
        int[] p,w,bestx;
        Scanner input = new Scanner(System.in);

        while (true){
            n = input.nextInt();
            c = input.nextInt();

            p = new int[n+1];
            w = new int[n+1];
            bestx = new int[n+1];

            for(int i=1; i<=n; i++)
                p[i] = input.nextInt();

            for(int i=1; i<=n; i++)
                w[i] = input.nextInt();

            for(int i=1; i<=n; i++)
                bestx[i] = 1;

            System.out.println(knapsack(p,w,c,n,bestx));
            for(int i=1; i<=n; i++)
                System.out.print(bestx[i]+" ");
        }
    }

    private static class UnitPrice implements Comparable{
        int ID;
        float d;

        public int compareTo(Object o){
            UnitPrice unitPrice = (UnitPrice) o;
            int result = Float.compare(unitPrice.d, d);

            return result;
        }
    }

    //返回最大价值,bestx返回最优解
    private static int knapsack(int[] p, int[] w, int c, int n, int[] bestx){
        int W = 0;
        int P = 0;
        //定义依单位重量价值排序的物品数组
        List<UnitPrice> Q = new ArrayList<>(n);
        for(int i=1; i<=n; i++){
            //单位重量价值数组
            UnitPrice tmp = new UnitPrice();
            tmp.ID = i;
            tmp.d = (float) (1.0*p[i]/w[i]);
            Q.add(tmp);
            P += p[i];
            W += w[i];
        }

        if(W <= c) return P;//所有物品装包
        //依单位重量价值排序
        Collections.sort(Q);

        Knap K = new Knap();
        K.p = new int[n+1];
        K.w = new int[n+1];

        for(int i=1; i<=n; i++){
            K.p[i] = p[Q.get(i-1).ID];
            K.w[i] = w[Q.get(i-1).ID];
        }
        K.cp=0; K.cw=0; K.c=c; K.n=n;

        K.pqbb();
        bestp = K.bestp;
        for(int j=1; j<=n; j++) bestx[Q.get(j-1).ID]=K.bestx[j];//还原

        return bestp;
    }
}

Input & Output

5 10
6 3 5 4 6
2 2 6 5 4
15
1 1 0 0 1 


225 1235
97 55 30 50 33 39 93 48 35 3 71 96 25 16 76 14 53 91 71 93 98 25 95 96 49 18 39 19 73 56 51 24 81 99 36 17 5 63 14 18 53 93 72 63 61 11 52 39 42 49 92 20 76 79 16 30 84 52 23 60 67 2 49 49 67 5 13 65 50 2 93 79 52 52 53 1 48 29 84 25 59 18 3 8 17 2 95 33 22 12 23 41 20 16 64 94 64 47 79 58 37 41 27 41 64 36 28 83 63 71 9 83 80 5 46 20 42 3 90 39 58 52 78 62 29 73 64 39 8 26 65 43 69 51 33 48 42 48 89 90 58 32 46 43 55 68 20 79 6 67 37 99 84 80 23 44 46 8 92 9 87 35 38 43 86 66 25 50 80 72 97 7 30 67 44 87 67 28 73 74 8 38 71 74 57 4 17 1 38 8 11 71 14 97 87 22 7 85 49 22 94 99 94 16 35 31 44 87 96 30 64 1 63 89 33 92 45 84 14 58 86 28 6 16 60 
54 89 55 2 65 96 6 31 11 48 75 30 73 26 5 4 22 38 31 96 74 13 16 52 77 29 28 93 22 58 61 22 41 97 33 46 67 49 87 49 70 38 69 3 54 40 67 63 50 14 19 87 78 71 11 18 75 60 61 55 23 95 47 2 2 18 80 43 62 86 4 81 29 63 99 7 68 79 30 6 67 14 12 81 78 54 14 63 61 19 97 44 42 88 18 24 95 41 45 6 45 80 36 72 46 24 63 40 41 33 55 46 14 95 81 27 54 35 84 65 50 26 56 73 8 14 3 82 10 74 61 56 51 79 83 53 4 50 45 94 31 97 6 89 40 81 33 14 88 81 59 3 17 32 1 52 86 53 26 48 44 14 73 59 2 73 55 39 10 57 8 10 95 56 81 58 49 76 70 80 72 21 27 44 60 27 94 49 68 92 53 50 49 4 47 40 24 57 68 47 70 42 66 91 82 83 10 6 20 88 53 92 70 3 45 22 98 63 86 56 10 17 98 72 53 
4591
1 0 0 1 0 0 1 0 1 0 0 1 0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0 1 0 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 0 0 

Reference

王晓东《计算机算法设计与分析》

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值