modify code

pull/6/head
左程云 4 years ago
parent 1f4ab45ed5
commit 355cdbd84f

@ -1,9 +1,8 @@
package class20; package class20;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.LinkedList;
// arr中的每个值都代表一张钱 // arr中的每个值都代表一张钱
// arr中都是正数aim>=0返回组成aim的最小张数 // arr中都是正数aim>=0返回组成aim的最小张数
@ -29,6 +28,7 @@ public class Code04_MinCoinsOnePaper {
} }
} }
// dp1时间复杂度为O(arr长度 * aim)
public static int dp1(int[] arr, int aim) { public static int dp1(int[] arr, int aim) {
if (aim == 0) { if (aim == 0) {
return 0; return 0;
@ -82,10 +82,12 @@ public class Code04_MinCoinsOnePaper {
return new Info(coins, zhangs); return new Info(coins, zhangs);
} }
// dp2时间复杂度为O(arr长度) + O(货币种数 * aim * 每种货币的平均张数)
public static int dp2(int[] arr, int aim) { public static int dp2(int[] arr, int aim) {
if (aim == 0) { if (aim == 0) {
return 0; return 0;
} }
// 得到info时间复杂度O(arr长度)
Info info = getInfo(arr); Info info = getInfo(arr);
int[] coins = info.coins; int[] coins = info.coins;
int[] zhangs = info.zhangs; int[] zhangs = info.zhangs;
@ -95,6 +97,7 @@ public class Code04_MinCoinsOnePaper {
for (int j = 1; j <= aim; j++) { for (int j = 1; j <= aim; j++) {
dp[N][j] = Integer.MAX_VALUE; dp[N][j] = Integer.MAX_VALUE;
} }
// 这三层for循环时间复杂度为O(货币种数 * aim * 每种货币的平均张数)
for (int index = N - 1; index >= 0; index--) { for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= aim; rest++) { for (int rest = 0; rest <= aim; rest++) {
dp[index][rest] = dp[index + 1][rest]; dp[index][rest] = dp[index + 1][rest];
@ -110,95 +113,51 @@ public class Code04_MinCoinsOnePaper {
} }
// 这种解法课上不讲 // 这种解法课上不讲
// 因为需要窗口内最大值和最小值的更新结构 // 因为需要理解窗口内最小值的更新结构
// 后面的课会讲 // 后面的课会讲
// dp3时间复杂度为O(arr长度) + O(货币种数 * aim)
public static int dp3(int[] arr, int aim) { public static int dp3(int[] arr, int aim) {
if (aim == 0) { if (aim == 0) {
return 0; return 0;
} }
// 得到info时间复杂度O(arr长度)
Info info = getInfo(arr); Info info = getInfo(arr);
int[] coins = info.coins; int[] c = info.coins;
int[] zhangs = info.zhangs; int[] z = info.zhangs;
int N = coins.length; int N = c.length;
int[][] dp = new int[N + 1][aim + 1]; int[][] dp = new int[N + 1][aim + 1];
dp[N][0] = 0; dp[N][0] = 0;
for (int j = 1; j <= aim; j++) { for (int j = 1; j <= aim; j++) {
dp[N][j] = Integer.MAX_VALUE; dp[N][j] = Integer.MAX_VALUE;
} }
int max = Integer.MIN_VALUE; // 虽然是嵌套了很多循环但是时间复杂度为O(货币种数 * aim)
for (int i = 0; i < N; i++) { // 因为用了窗口内最小值的更新结构
max = Math.max(max, coins[i]); for (int i = N - 1; i >= 0; i--) {
for (int mod = 0; mod < Math.min(aim + 1, c[i]); mod++) {
LinkedList<Integer> w = new LinkedList<>();
w.add(mod);
dp[i][mod] = dp[i + 1][mod];
for (int r = mod + c[i]; r <= aim; r += c[i]) {
while (!w.isEmpty() && (dp[i + 1][w.peekLast()] == Integer.MAX_VALUE
|| dp[i + 1][w.peekLast()] + compensate(w.peekLast(), r, c[i]) >= dp[i + 1][r])) {
w.pollLast();
} }
WindowBoss windows = new WindowBoss(max); w.addLast(r);
for (int index = N - 1; index >= 0; index--) { int overdue = r - c[i] * (z[i] + 1);
windows.setDpCoinZhang(dp[index + 1], coins[index], zhangs[index]); if (w.peekFirst() == overdue) {
int rest = 0; w.pollFirst();
for (; rest < Math.min(aim + 1, coins[index]); rest++) {
windows.clearAdd(rest);
dp[index][rest] = dp[index + 1][rest];
}
for (; rest <= aim; rest++) {
windows.add(rest);
dp[index][rest] = windows.min(rest);
} }
dp[i][r] = dp[i + 1][w.peekFirst()] + compensate(w.peekFirst(), r, c[i]);
} }
return dp[0][aim];
} }
// 改进的窗口内最大值和最小值的更新结构
public static class WindowBoss {
public ArrayList<LinkedList<Integer>> windows;
private int[] dp;
private int coin;
private int zhang;
public WindowBoss(int maxValue) {
windows = new ArrayList<>();
for (int i = 0; i < maxValue; i++) {
windows.add(new LinkedList<>());
} }
dp = null; return dp[0][aim];
coin = 0;
zhang = 0;
} }
private int offset(int pre, int cur) { public static int compensate(int pre, int cur, int coin) {
return (cur - pre) / coin; return (cur - pre) / coin;
} }
public void setDpCoinZhang(int[] d, int c, int z) {
dp = d;
coin = c;
zhang = z;
}
public void clearAdd(int rest) {
int windowi = rest % coin;
windows.get(windowi).clear();
windows.get(windowi).addLast(rest);
}
public void add(int rest) {
LinkedList<Integer> window = windows.get(rest % coin);
while (!window.isEmpty() && (dp[window.peekLast()] == Integer.MAX_VALUE
|| dp[window.peekLast()] + offset(window.peekLast(), rest) >= dp[rest])) {
window.pollLast();
}
window.addLast(rest);
int overdue = rest - coin * (zhang + 1);
if (window.peekFirst() == overdue) {
window.pollFirst();
}
}
public int min(int rest) {
LinkedList<Integer> window = windows.get(rest % coin);
int minIndex = window.peekFirst();
return dp[minIndex] + offset(minIndex, rest);
}
}
// 为了测试 // 为了测试
public static int[] randomArray(int N, int maxValue) { public static int[] randomArray(int N, int maxValue) {
int[] arr = new int[N]; int[] arr = new int[N];
@ -287,7 +246,7 @@ public class Code04_MinCoinsOnePaper {
System.out.println("当货币很少出现重复dp2比dp3有常数时间优势"); System.out.println("当货币很少出现重复dp2比dp3有常数时间优势");
System.out.println("当货币大量出现重复dp3时间复杂度明显优于dp2"); System.out.println("当货币大量出现重复dp3时间复杂度明显优于dp2");
System.out.println("dp3的讲解放在窗口内最大值最小值的更新结构里"); System.out.println("dp3的讲解放在窗口内最大值最小值的更新结构里");
} }
} }

Loading…
Cancel
Save