平分娃娃 - 多重背包问题

蒜头君酷爱收集萌萌的娃娃。蒜头君收集了 6 种不同的娃娃,第 i 种娃娃的萌值为 i(1≤ i ≤6)。现在已知每种娃娃的数量 mi,蒜头君想知道,能不能把娃娃分成两组,使得每组的娃娃萌值之和相同。

输入格式
输入一行,输入 6 个整数,代表每种娃娃的数量mi(0≤ mi ≤20,000)。
输出格式
输出一行。如果能把所有娃娃分成萌值之和相同的两组,请输出“Can be divided.”,否则输出“Can’t be divided.”。

样例输入1
2 2 2 2 2 2
样例输出1

Can be divided

样例输入2
2 2 2 2 2 2
样例输出2
Can be divided

这个题的本质就是01背包问题,关键点在于把已知的允许重复存在的物品列表,扩大拆分成每一种物品只允许有一个的列表。

因为知道每一种物品的数量,那就直接在标准输入的for循环中进行“n += num[i];”操作,最后得到的n就是拆分之后得到的物品列表的长度。之后虚拟出一个“可容纳萌值数是总萌值数的一半的”袋子,再通过动态规划求到这个袋子可以装到的最大萌值数。如果装的最大萌值数正好等于总萌值数的一半,那么就说明可以被平分。

但是,这种思路只能通过前四组测试数据,第五组测试数据会显示时间超时。

怎么优化时间复杂度呢?

这里使用的是二进制优化,就是将原本有n件的物品,变成log2n件,例如:原本有14件物品A,转换为:1*A,2*A,4*A和7*A,每个物品A和其前面的系数归并成一件物品,这样就把14件物品转化成4件物品了。

其结构为:{A, A, ... ... , A} >> {1 * A, 2 * A, 4 * A, ... ..., 2^(k - 1) * A, (n - 2^k + 1) * A}.

*左边为长度为n的物品A的数组

之后再进行同样的操作,就可以得到答案。


Java代码:

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int[] num = new int[7];
        int sum = 0;
        int n = 0;
        for(int i = 1; i <= 6; i++) {
            num[i] = in.nextInt();
            sum += num[i] * i;
            n += num[i];
        }
        in.close();
        if(sum % 2 != 0) {
            System.out.println("Can't be divided.");
        }else {
            int V = sum / 2;
            
            int[] item = new int[n + 1];
            
            int count = 1;
            
            for(int i = 1; i <= 6; i++) {                
                int k = 0;
                while(true) {
                    if(Math.pow(2, k) < num[i] + 1)
                        k++;
                    else    
                        break;
                }
                
                int muti = 1;
                for(int j = 1; j <= k; j++) {
                    if(j == k) {
                        item[count] = (int) (i * (num[i] + 1 - Math.pow(2, k - 1)));
                    }else {
                        item[count] = i * muti;
                        muti *= 2;                        
                    }
                    count++;
                }
            }
            
            int[] f = new int[V + 1];

            for(int i = 1; i < count; i++) {
                for(int j = V; j >= item[i]; j--) {
                    f[j] = Math.max(f[j], f[j - item[i]] + item[i]);
                    
                }
            }
            if(f[V] == V) {
                System.out.println("Can be divided.");
            }else {
                System.out.println("Can't be divided.");
            }
        }
    }
}

你可能感兴趣的:(平分娃娃 - 多重背包问题)