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.

309 lines
8.8 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 class27;
import java.util.Arrays;
import java.util.HashMap;
public class Code01_PickBands {
// 每一个项目都有三个数,[a,b,c]表示这个项目a和b乐队参演花费为c
// 每一个乐队可能在多个项目里都出现了,但是只能挑一次
// nums是可以挑选的项目数量所以一定会有nums*2只乐队被挑选出来
// 乐队的全部数量一定是nums*2且标号一定是0 ~ nums*2-1
// 返回一共挑nums轮(也就意味着一定请到所有的乐队),最少花费是多少?
public static int minCost(int[][] programs, int nums) {
if (nums == 0 || programs == null || programs.length == 0) {
return 0;
}
int size = clean(programs);
int[] map1 = init(1 << (nums << 1));
int[] map2 = null;
if ((nums & 1) == 0) {
// nums = 8 , 4
f(programs, size, 0, 0, 0, nums >> 1, map1);
map2 = map1;
} else {
// nums == 7 4 -> map1 3 -> map2
f(programs, size, 0, 0, 0, nums >> 1, map1);
map2 = init(1 << (nums << 1));
f(programs, size, 0, 0, 0, nums - (nums >> 1), map2);
}
// 16 mask : 0..00 1111.1111(16个)
// 12 mask : 0..00 1111.1111(12个)
int mask = (1 << (nums << 1)) - 1;
int ans = Integer.MAX_VALUE;
for (int i = 0; i < map1.length; i++) {
if (map1[i] != Integer.MAX_VALUE && map2[mask & (~i)] != Integer.MAX_VALUE) {
ans = Math.min(ans, map1[i] + map2[mask & (~i)]);
}
}
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// [
// [9, 1, 100]
// [2, 9 , 50]
// ...
// ]
public static int clean(int[][] programs) {
int x = 0;
int y = 0;
for (int[] p : programs) {
x = Math.min(p[0], p[1]);
y = Math.max(p[0], p[1]);
p[0] = x;
p[1] = y;
}
Arrays.sort(programs, (a, b) -> a[0] != b[0] ? (a[0] - b[0]) : (a[1] != b[1] ? (a[1] - b[1]) : (a[2] - b[2])));
x = programs[0][0];
y = programs[0][1];
int n = programs.length;
// (0, 1, ? )
for (int i = 1; i < n; i++) {
if (programs[i][0] == x && programs[i][1] == y) {
programs[i] = null;
} else {
x = programs[i][0];
y = programs[i][1];
}
}
int size = 1;
for (int i = 1; i < n; i++) {
if (programs[i] != null) {
programs[size++] = programs[i];
}
}
// programs[0...size-1]
return size;
}
public static int[] init(int size) {
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = Integer.MAX_VALUE;
}
return arr;
}
public static void f(int[][] programs, int size, int index, int status, int cost, int rest, int[] map) {
if (rest == 0) {
map[status] = Math.min(map[status], cost);
} else {
if (index != size) {
f(programs, size, index + 1, status, cost, rest, map);
int pick = 0 | (1 << programs[index][0]) | (1 << programs[index][1]);
if ((pick & status) == 0) {
f(programs, size, index + 1, status | pick, cost + programs[index][2], rest - 1, map);
}
}
}
}
// // 如果nums2 * nums 只乐队
// // (1 << (nums << 1)) - 1
// // programs 洗数据 size
// // nums = 8 16只乐队
//
// // process(programs, size, (1 << (nums << 1)) - 1, 0, 4, 0, 0)
//
// public static int minCost = Integer.MAX_VALUE;
//
// public static int[] map = new int[1 << 16]; // map初始化全变成系统最大
//
//
//
// public static void process(int[][] programs, int size, int index, int rest, int pick, int cost) {
// if (rest == 0) {
//
// map[pick] = Math.min(map[pick], cost);
//
// } else { // 还有项目可挑
// if (index != size) {
// // 不考虑当前的项目programs[index];
// process(programs, size, index + 1, rest, pick, cost);
// // 考虑当前的项目programs[index];
// int x = programs[index][0];
// int y = programs[index][1];
// int cur = (1 << x) | (1 << y);
// if ((pick & cur) == 0) { // 终于可以考虑了!
// process(programs, size, index + 1, rest - 1, pick | cur, cost + programs[index][2]);
// }
// }
// }
// }
//
//
// public static void zuo(int[] arr, int index, int rest) {
// if(rest == 0) {
// 停止
// }
// if(index != arr.length) {
// zuo(arr, index + 1, rest);
// zuo(arr, index + 1, rest - 1);
// }
// }
// 同学提供的解,很不错
// 不做要求
public static HashMap<Integer, Integer> cache = new HashMap<>();
public static int minCost2(int[][] programs, int nums) {
// 调整成 programs[i][0] < programs[i][1]
for (int i = 0; i < programs.length; i++) {
if (programs[i][0] > programs[i][1]) {
int t = programs[i][0];
programs[i][0] = programs[i][1];
programs[i][1] = t;
}
}
// 排序
Arrays.sort(programs,
((o1, o2) -> o1[0] - o2[0] != 0 ? o1[0] - o2[0] : o1[1] - o2[1] != 0 ? o1[1] - o2[1] : o1[2] - o2[2]));
cache.clear();
return minCost2(programs, 0, nums, 0);
}
// 返回 [index, 最后], 选择 nums 次, 的最小花费, 已经选择过的队记录在 status 中
// 如果无法满足题意, 返回 -1
// 看似有三个参数, 其实 nums 是可以根据 status 算出来的, 是冗余的
public static int minCost2(int[][] programs, int index, int nums, int status) {
if (nums == 0) {
return 0;
} else if (index == programs.length) {
return -1;
}
Integer key = status * programs.length + index;
Integer ans = cache.get(key);
if (ans != null) {
return ans;
} else {
ans = Integer.MAX_VALUE;
}
// 对于 [index, next-1] 范围的项目, programs[ ][0] 是一样的, 假设都为 乐队x
int next = index + 1;
while (next < programs.length && programs[index][0] == programs[next][0]) {
next++;
}
if ((status & (1 << programs[index][0])) != 0) {
return minCost2(programs, next, nums, status);
}
status |= 1 << programs[index][0];
for (int i = index; i < next; i++) {
// 一个项目的两个乐队相同, 只取最优的
if (i - 1 >= index && programs[i][1] == programs[i - 1][1]) {
continue;
}
if ((status & (1 << programs[i][1])) == 0) {
// 选择当前项目, 并跳到 next
int t = minCost2(programs, next, nums - 1, status | 1 << programs[i][1]);
if (t != -1) {
ans = Math.min(ans, t + programs[i][2]);
}
}
}
// 算到这里, 如果 ans == Integer.MAX_VALUE, 由于后续过程 乐队x 不会再出现, 返回-1
ans = ans == Integer.MAX_VALUE ? -1 : ans;
cache.put(key, ans);
return ans;
}
// 为了测试
public static int right(int[][] programs, int nums) {
min = Integer.MAX_VALUE;
r(programs, 0, nums, 0, 0);
return min == Integer.MAX_VALUE ? -1 : min;
}
public static int min = Integer.MAX_VALUE;
public static void r(int[][] programs, int index, int rest, int pick, int cost) {
if (rest == 0) {
min = Math.min(min, cost);
} else {
if (index < programs.length) {
r(programs, index + 1, rest, pick, cost);
int cur = (1 << programs[index][0]) | (1 << programs[index][1]);
if ((pick & cur) == 0) {
r(programs, index + 1, rest - 1, pick | cur, cost + programs[index][2]);
}
}
}
}
// 为了测试
public static int[][] randomPrograms(int N, int V) {
int nums = N << 1;
int n = nums * (nums - 1);
int[][] programs = new int[n][3];
for (int i = 0; i < n; i++) {
int a = (int) (Math.random() * nums);
int b = 0;
do {
b = (int) (Math.random() * nums);
} while (b == a);
programs[i][0] = a;
programs[i][1] = b;
programs[i][2] = (int) (Math.random() * V) + 1;
}
return programs;
}
// 为了测试
public static int[][] copyPrograms(int[][] programs) {
int n = programs.length;
int[][] ans = new int[n][3];
for (int i = 0; i < n; i++) {
ans[i][0] = programs[i][0];
ans[i][1] = programs[i][1];
ans[i][2] = programs[i][2];
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 4;
int V = 100;
int T = 10000;
System.out.println("测试开始");
for (int i = 0; i < T; i++) {
int nums = (int) (Math.random() * N) + 1;
int[][] programs1 = randomPrograms(nums, V);
int[][] programs2 = copyPrograms(programs1);
int[][] programs3 = copyPrograms(programs1);
int ans1 = right(programs1, nums);
int ans2 = minCost(programs2, nums);
int ans3 = minCost2(programs3, nums);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束");
long start;
long end;
int[][] programs;
programs = randomPrograms(7, V);
start = System.currentTimeMillis();
right(programs, 7);
end = System.currentTimeMillis();
System.out.println("right方法在nums=7时候的运行时间(毫秒) : " + (end - start));
programs = randomPrograms(10, V);
start = System.currentTimeMillis();
minCost(programs, 10);
end = System.currentTimeMillis();
System.out.println("minCost方法在nums=10时候的运行时间(毫秒) : " + (end - start));
programs = randomPrograms(10, V);
start = System.currentTimeMillis();
minCost2(programs, 10);
end = System.currentTimeMillis();
System.out.println("minCost2方法在nums=10时候的运行时间(毫秒) : " + (end - start));
}
}