蒜头君酷爱收集萌萌的娃娃。蒜头君收集了 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.");
}
}
}
}