modify code

master
algorithmzuo 2 years ago
parent fb03c1258f
commit e5bfd19b9f

@ -1,6 +1,7 @@
// 不要拷贝包信息的内容 // 不要拷贝包信息的内容
package class39; package class39;
// 课堂版本
// 本文件是Code02_SnacksWays问题的牛客题目解答 // 本文件是Code02_SnacksWays问题的牛客题目解答
// 但是用的分治的方法 // 但是用的分治的方法
// 这是牛客的测试链接: // 这是牛客的测试链接:
@ -11,7 +12,7 @@ import java.util.Map.Entry;
import java.util.Scanner; import java.util.Scanner;
import java.util.TreeMap; import java.util.TreeMap;
public class Code02_SnacksWaysMain { public class Code02_SnacksWaysMain1 {
public static void main(String[] args) { public static void main(String[] args) {
Scanner sc = new Scanner(System.in); Scanner sc = new Scanner(System.in);

@ -0,0 +1,135 @@
// 不要拷贝包信息的内容
package class39;
//优化版本
import java.util.Arrays;
import java.util.Scanner;
public class Code02_SnacksWaysMain2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
size = sc.nextInt();
long w = (long) sc.nextInt();
for (int i = 0; i < size; i++) {
arr[i] = (long) sc.nextInt();
}
long ways = ways(w);
System.out.println(ways);
}
sc.close();
}
// 用来收集所有输入的数字
public static long[] arr = new long[31];
public static int size = 0;
// 用来生成左部分可能的所有累加和
public static long[] leftSum = new long[1 << 16];
// 准备的数组可能用不完左部分生成了多少累加和用leftSize表示
public static int leftSize = 0;
// 用来生成右部分可能的所有累加和
public static long[] rightSum = new long[1 << 16];
// 准备的数组可能用不完左部分生成了多少累加和用leftSize表示
public static int rightSize = 0;
public static long ways(long w) {
if (size == 0) {
return 0;
}
if (size == 1) {
return arr[0] <= w ? 2 : 1;
}
// 求中点
int mid = size >> 1;
// 生成左侧的累加和
leftSize = 0;
dfsLeft(0, mid + 1, 0L);
// 生成右侧的累加和
rightSize = 0;
dfsRight(mid + 1, size, 0L);
// 把左侧累加和排序
Arrays.sort(leftSum, 0, leftSize);
// 把右侧累加和排序
Arrays.sort(rightSum, 0, rightSize);
// 解释一下,接下来的流程。
// 举个例子,比如:
// 左侧累加和是:{0, 1, 1, 1, 2, 2, 3, 4, 4}
// 右侧累加和是:{0, 1, 2, 3, 3, 3, 4, 4, 5}
// w = 5
// 左侧严格得到0的方法数1
// 右侧得到<=5的方法数二分求出9
// 1 * 9
// 左侧严格得到1的方法数3
// 右侧得到<=4的方法数二分求出8
// 3 * 8
// 左侧严格得到2的方法数2
// 右侧得到<=3的方法数二分求出6
// 2 * 6
// 左侧严格得到3的方法数1
// 右侧得到<=2的方法数二分求出3
// 1 * 3
// 左侧严格得到4的方法数2
// 右侧得到<=1的方法数二分求出2
// 2 * 2
// 都累加起来
// 其实和课上讲的一样!多看一下例子
long ans = 0;
long count = 1;
for (int i = 1; i < leftSize; i++) {
if (leftSum[i] != leftSum[i - 1]) {
ans += count * (long) find(w - leftSum[i - 1]);
count = 1;
} else {
count++;
}
}
ans += count * (long) find(w - leftSum[leftSize - 1]);
return ans;
}
// 生成左部分的累加和,每一个累加和出来,都记录
public static void dfsLeft(int cur, int end, long sum) {
if (cur == end) { // 已经终止位置了
// 记录累加和
leftSum[leftSize++] = sum;
} else {
// 可能性1不要当前数
dfsLeft(cur + 1, end, sum);
// 可能性2要当前数
dfsLeft(cur + 1, end, sum + arr[cur]);
}
}
// 生成右部分的累加和,每一个累加和出来,都记录
public static void dfsRight(int cur, int end, long sum) {
if (cur == end) { // 已经终止位置了
// 记录累加和
rightSum[rightSize++] = sum;
} else {
// 可能性1不要当前数
dfsRight(cur + 1, end, sum);
// 可能性2要当前数
dfsRight(cur + 1, end, sum + arr[cur]);
}
}
// <= num的数的个数返回
public static int find(long num) {
int ans = -1;
int l = 0;
int r = rightSize - 1;
int m = 0;
while (l <= r) {
m = (l + r) / 2;
if (rightSum[m] <= num) {
ans = m;
l = m + 1;
} else {
r = m - 1;
}
}
return ans + 1;
}
}
Loading…
Cancel
Save