You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 不要拷贝包信息的内容
package class39;
// 优化版本
// 这是牛客的测试链接:
// https://www.nowcoder.com/questionTerminal/d94bb2fa461d42bcb4c0f2b94f5d4281
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交如下的代码,并把主类名改成"Main"
// 可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Code02_SnacksWaysMain2 {
// 用来收集所有输入的数字
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 void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
size = (int) in.nval;
in.nextToken();
int bag = (int) in.nval;
for (int i = 0; i < size; i++) {
in.nextToken();
arr[i] = (int) in.nval;
}
long ways = ways(bag);
out.println(ways);
out.flush();
}
}
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;
}
}