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.

237 lines
6.0 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 class_2022_04_2_week;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
// 来自快手
// 某公司年会上大家要玩一食发奖金游戏一共有n个员工
// 每个员工都有建设积分和捣乱积分
// 他们需要排成一队,在队伍最前面的一定是老板,老板也有建设积分和捣乱积分
// 排好队后,所有员工都会获得各自的奖金,
// 该员工奖金 = 排在他前面所有人的建设积分乘积 / 该员工自己的捣乱积分,向下取整
// 为了公平(放屁),老板希望 : 让获得奖金最高的员工,所获得的奖金尽可能少
// 所以想请你帮他重新排一下队伍,返回奖金最高的员工获得的、尽可能少的奖金数额
// 快手考试的时候,给定的数据量,全排列的代码也能过的!
// 1 <= n <= 1000, 1<= 积分 <= 10000;
public class Code07_MaxMoneyMostMin {
// 暴力方法
// 为了验证
// a : 老板的贡献积分
// b : 老板的捣乱积分
// value[i] : i号员工的贡献积分
// trouble[i] : i号员工的捣乱积分
// 返回 : 奖金最高的员工获得的、尽可能少的奖金数额
public static long mostMin1(int a, int b, int[] value, int[] trouble) {
return process1(a, value, trouble, 0);
}
public static long process1(int boss, int[] value, int[] trouble, int index) {
if (index == value.length) {
long valueAll = boss;
long ans = 0;
for (int i = 0; i < value.length; i++) {
ans = Math.max(ans, valueAll / trouble[i]);
valueAll *= value[i];
}
return ans;
} else {
long ans = Long.MAX_VALUE;
for (int i = index; i < value.length; i++) {
swap(value, trouble, i, index);
ans = Math.min(ans, process1(boss, value, trouble, index + 1));
swap(value, trouble, i, index);
}
return ans;
}
}
public static void swap(int[] value, int[] trouble, int i, int j) {
int tmp = value[i];
value[i] = value[j];
value[j] = tmp;
tmp = trouble[i];
trouble[i] = trouble[j];
trouble[j] = tmp;
}
// 正式方法
// 所有员工数量为N
// 假设所有员工建设积分乘起来为M
// 时间复杂度O(N * logN * logM)
public static long mostMin2(int a, int b, int[] value, int[] trouble) {
int n = value.length;
long l = 0;
long r = 0;
long valueAll = a;
long[][] staff = new long[n][2];
for (int i = 0; i < n; i++) {
r = Math.max(r, valueAll / trouble[i]);
valueAll *= value[i];
staff[i][0] = (long) value[i] * (long) trouble[i];
staff[i][1] = value[i];
}
Arrays.sort(staff, (x, y) -> (y[0] >= x[0] ? 1 : -1));
long m = 0;
long ans = 0;
while (l <= r) {
m = l + ((r - l) >> 1);
if (yeah(valueAll, staff, m)) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// staff长度为N时间复杂度O(N * logN)
public static boolean yeah(long all, long[][] staff, long limit) {
int n = staff.length;
SegmentTree st = new SegmentTree(n);
HashMap<Integer, LinkedList<Integer>> map = new HashMap<>();
for (int i = 0, index = 1; i < n; i++, index++) {
int value = (int) staff[i][1];
st.update(index, value);
if (!map.containsKey(value)) {
map.put(value, new LinkedList<>());
}
map.get(value).addLast(index);
}
for (int k = 0; k < n; k++) {
int right = boundary(staff, all, limit);
if (right == 0) {
return false;
}
int max = st.max(right);
if (max == 0) {
return false;
}
int index = map.get(max).pollFirst();
st.update(index, 0);
all /= max;
}
return true;
}
public static int boundary(long[][] staff, long all, long limit) {
int l = 0;
int r = staff.length - 1;
int m = 0;
int ans = -1;
while (l <= r) {
m = l + ((r - l) >> 1);
if (all / staff[m][0] <= limit) {
ans = m;
l = m + 1;
} else {
r = m - 1;
}
}
return ans + 1;
}
public static class SegmentTree {
private int n;
private int[] max;
private int[] update;
public SegmentTree(int maxSize) {
n = maxSize + 1;
max = new int[n << 2];
update = new int[n << 2];
Arrays.fill(update, -1);
}
public void update(int index, int c) {
update(index, index, c, 1, n, 1);
}
public int max(int right) {
return max(1, right, 1, n, 1);
}
private void pushUp(int rt) {
max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
}
private void pushDown(int rt, int ln, int rn) {
if (update[rt] != -1) {
update[rt << 1] = update[rt];
max[rt << 1] = update[rt];
update[rt << 1 | 1] = update[rt];
max[rt << 1 | 1] = update[rt];
update[rt] = -1;
}
}
private void update(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
max[rt] = C;
update[rt] = C;
return;
}
int mid = (l + r) >> 1;
pushDown(rt, mid - l + 1, r - mid);
if (L <= mid) {
update(L, R, C, l, mid, rt << 1);
}
if (R > mid) {
update(L, R, C, mid + 1, r, rt << 1 | 1);
}
pushUp(rt);
}
private int max(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return max[rt];
}
int mid = (l + r) >> 1;
pushDown(rt, mid - l + 1, r - mid);
int ans = 0;
if (L <= mid) {
ans = Math.max(ans, max(L, R, l, mid, rt << 1));
}
if (R > mid) {
ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1));
}
return ans;
}
}
// 为了测试
public static int[] randomArray(int len, int value) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * value) + 1;
}
return arr;
}
// 为了测试
public static void main(String[] args) {
int n = 9;
int v = 50;
int testTime = 5000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int a = (int) (Math.random() * v) + 1;
int b = (int) (Math.random() * v) + 1;
int len = (int) (Math.random() * n);
int[] value = randomArray(len, v);
int[] trouble = randomArray(len, v);
long ans1 = mostMin1(a, b, value, trouble);
long ans2 = mostMin2(a, b, value, trouble);
if (ans1 != ans2) {
System.out.println("出错了!");
break;
}
}
System.out.println("测试结束");
}
}