多重背包的二进制优化

多重背包的二进制优化

现在有价值分别为1到6的六种物品,数量分别是n1,n2,n3,n4,n5,n6,问是否可以把这些物品分成价值相等的两份?

输入格式

输入中的每一行描述了要分割的一个物品集合。这些物品由六个非负整数n1,n2,…,n6组成,其中ni是价值i的物品数。最大物品总数为20000。 输入文件的最后一行是“0 0 0 0 0 0”;不要处理这一行。

输出格式

1 0 1 2 0 0 
1 0 0 0 1 1 
0 0 0 0 0 0

对应结果

NO
YES

思路

多重背包暴力解法时间复杂度为O(N * V * S)

当物品数量较大时,一个一个的选择浪费了太多的时间,必然超时。

我们采用二进制优化的思想,将同种类的物体按照二进制进行分组,每一组合并成一个新的价值和重量的物品,第 i 种物品按照以下数量分组:1,2,4,8,16...... 2^k......512 ( 2 0 , 2 1 , 2 2 , 2 3 , . . . 2 k , . . . 2 9 2^0, 2^1, 2^2, 2^3,...2^k,...2^9 20,21,22,23,...2k,...29)此时 (1~1023) 中的每一个数都可以通过以上分组的组合表示出来(二进制)。

例子:

19 = ( 10011 ) 2 = 2 0 + 2 1 + 2 4 19 = (10011)_2 = 2^0 + 2^1 + 2^4 19=(10011)2=20+21+24

当有剩余数量无法被2的幂次方表示时

例子:第 i 种物品有200个:

可以被2的幂次方表示的有:1,2,4,8...64

1,2,4,8...64,可以使用2的幂次方表示出来,可以表示出来 (1~127) 中的所有数,这是还没使用完200这个数,还有剩余的个数为200-127 = 73个,将其单独成新的一组,就可以表示出 (1~200) 中的任意数。

最终的分组情况为:1,2,4,8...64,73

而每一组合并成了一个新的物品,新的物品的价值为原来物品的价值 * 个数,新物品的重量为原来物品的重量 * 个数,以上述200个物品为例,假设该物品价值为2,重量为3,则新物品的价值分别是:2,4,8,16...128,146,新物品的重量分别是:3,6,12,24,192,219

所以,按照二进制的形式分组选择,加快选取速度。

并且对于该题,物品的价值和体积相等

代码实现

import java.util.Scanner;

public class Main {
    public static void main(String[] args)  {
        Scanner sc = new Scanner(System.in);
        while (true) {
            //价值数组
            int[] v = new int[10000];
            //记录价值数组下标
            int index = 0;
            //所有物品价值总和
            int sum = 0;
            for(int i = 0;  i < 6; i++) {
                int num = sc.nextInt();
                sum += num * (i + 1);
                //二进制分组
                for(int k = 1; k <= num; k *= 2, num -= k, index++) {
                    v[index] = k * (i + 1);
                }
                //无法用2的幂次方表示的单独再分一组
                if(num > 0) {
                    v[index++] = num * (i + 1);
                }
            }
            if (sum == 0) {
                break;
            }
            sc.nextLine();
            //总和是奇数的一定不能分成相等的两份
            if(sum % 2 != 0) {
                System.out.println("NO");
                continue;
            }
            int bagWeigh = sum / 2;
            int[] dp = new int[bagWeigh + 1];
            for(int i = 0; i < index; i++) {
                for(int j = bagWeigh; j >= v[i]; j--) {
                    dp[j] = Math.max(dp[j - v[i]] + v[i], dp[j]);
                }
            }
            if(dp[bagWeigh] == bagWeigh) {
                System.out.println("YES");
            }else {
                System.out.println("NO");
            }
        }
        sc.close();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值