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

2 years ago
// 不要拷贝包信息的内容
package class39;
2 years ago
// 优化版本
// 这是牛客的测试链接:
// 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;
2 years ago
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;
2 years ago
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();
}
}
2 years ago
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;
}
}