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.

158 lines
4.5 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_09_3_week;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
// 来自美团
// 某天小美进入了一个迷宫探险,根据地图所示,这个迷宫里有无数个房间
// 序号分别为1、2、3、...入口房间的序号为1
// 任意序号为正整数x的房间都与序号 2*x 和 2*x + 1 的房间之间各有一条路径
// 但是这些路径是单向的即只能从序号为x的房间去到序号为 2*x 或 2*x+1 的房间
// 而不能从 2*x 或 2*x+1 的房间去到序号为x的房间
// 在任何时刻小美都可以选择结束探险并离开迷宫,但是离开之后将无法再次进入迷宫
// 小美还提前了解了迷宫中宝藏的信息
// 已知宝藏共有n个其中第i个宝藏在序号为pi的房间价值为wi
// 且一个房间中可能有多个宝藏
// 小美为了得到更多的宝藏,需要精心规划路线,她找到你帮忙
// 想请你帮她计算一下,能获得的宝藏价值和最大值为多少
// 第一行一个正整数n表示宝藏数量。
// 第二行为n个正整数p1, p2,...... pn其中pi表示第 i 个宝藏在序号为pi的房间。
// 第三行为n个正整数w1, w2,...... wn其中wi表示第i个宝藏的价值为wi。
// 1 <= n <= 40000, 1 <= pi < 2^30, 1 <= wi <= 10^6。
public class Code02_EntryRoomGetMoney {
// 为了测试
// 普通动态规划
public static int maxMoney1(int n, int[] p, int[] w) {
int[][] rooms = new int[n][2];
for (int i = 0; i < n; i++) {
rooms[i][0] = p[i];
rooms[i][1] = w[i];
}
Arrays.sort(rooms, (a, b) -> a[0] - b[0]);
int ans = 0;
int[] dp = new int[n];
Arrays.fill(dp, -1);
for (int i = 0; i < n; i++) {
ans = Math.max(ans, process1(i, rooms, n, dp));
}
return ans;
}
public static int process1(int index, int[][] rooms, int n, int[] dp) {
if (dp[index] != -1) {
return dp[index];
}
int next = 0;
for (int i = index + 1; i < n; i++) {
if (reach(rooms[index][0], rooms[i][0])) {
next = Math.max(next, process1(i, rooms, n, dp));
}
}
int ans = rooms[index][1] + next;
dp[index] = ans;
return dp[index];
}
public static boolean reach(int from, int to) {
while (to >= from) {
if (from == to) {
return true;
} else {
to /= 2;
}
}
return false;
}
// 正式方法
// 时间复杂度O(N)的动态规划
// 利用图来优化枚举
public static int maxMoney2(int n, int[] p, int[] w) {
int[][] rooms = new int[n][2];
for (int i = 0; i < n; i++) {
rooms[i][0] = p[i];
rooms[i][1] = w[i];
}
Arrays.sort(rooms, (a, b) -> a[0] - b[0]);
HashMap<Integer, Integer> first = new HashMap<>();
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
int to = rooms[i][0];
while (to > 0) {
if (first.containsKey(to)) {
graph.get(first.get(to)).add(i);
break;
} else {
to >>= 1;
}
}
graph.add(new ArrayList<>());
if (!first.containsKey(rooms[i][0])) {
first.put(rooms[i][0], i);
}
}
int ans = 0;
int[] dp = new int[n];
for (int i = n - 1; i >= 0; i--) {
int post = 0;
for (int next : graph.get(i)) {
if (rooms[next][0] == rooms[i][0]) {
dp[i] += dp[next];
} else {
post = Math.max(post, dp[next]);
}
}
dp[i] += post + rooms[i][1];
ans = Math.max(ans, dp[i]);
}
return ans;
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v) + 1;
}
return ans;
}
public static void main(String[] args) {
int N = 100;
int P = 5000;
int W = 5000;
int testTimes = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] p = randomArray(n, P);
int[] w = randomArray(n, W);
int ans1 = maxMoney1(n, p, w);
int ans2 = maxMoney2(n, p, w);
if (ans1 != ans2) {
System.out.println("出错了");
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
N = 50000;
P = 2000000000;
W = 1000000;
int[] p = randomArray(N, P);
int[] w = randomArray(N, W);
System.out.println("房间个数 : " + N);
System.out.println("位置范围 : " + P);
System.out.println("价值范围 : " + W);
long start = System.currentTimeMillis();
maxMoney2(N, p, w);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}