AcWing 524. 憤怒的小鳥
阿新 • • 發佈:2022-05-12
題目連結:
https://www.acwing.com/problem/content/description/526/
題解:
記憶化搜尋比較好理解
狀態轉移DP只對全1情況有最優解
AC程式碼:
記憶化搜尋
// 記憶化搜尋 import java.util.*; public class Main { static int N = 20, M = 1 << 18; static PII[] pr = new PII[N]; static int[][] path = new int[N][N]; static int[] f = new int[M]; static int n, m; static void init() { Arrays.fill(f, 0x3f3f3f3f); for (int i = 0; i < N; i ++) Arrays.fill(path[i], 0); } static int cmp(double a, double b) { if (Math.abs(a - b) < 1e-6) return 0; if (a > b) return 1; return -1; } // 含義是,從st狀態轉移到全1狀態所需的最小步數 static int dfs(int st) { if (st == (1 << n) - 1) return 0; if (f[st] != 0x3f3f3f3f) return f[st]; int k = -1; for (int i = 0; i < n; i ++) { if ((st >> i & 1) == 0) { k = i; break; } } for (int j = 0; j < n; j ++) { if ((path[k][j] & 1 << k) != 0) f[st] = Math.min(f[st], dfs(st | path[k][j]) + 1); } return f[st]; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int T = sc.nextInt(); while(T -- > 0) { init(); n = sc.nextInt(); m = sc.nextInt(); for (int i = 0; i < n; i ++) pr[i] = new PII(sc.nextDouble(), sc.nextDouble()); for (int i = 0; i < n; i ++) { path[i][i] = 1 << i; for (int j = 0; j < n; j ++) { double x1 = pr[i].x, y1 = pr[i].y; double x2 = pr[j].x, y2 = pr[j].y; if (cmp(x1, x2) == 0) continue; double a = (y1 / x1 - y2 / x2) / (x1 - x2); if (cmp(a, 0.0) >= 0) continue; double b = (y1 / x1) - (a * x1); for (int k = 0; k < n; k ++) { double x3 = pr[k].x, y3 = pr[k].y; double yy = a * x3 * x3 + b * x3; if (cmp(yy, y3) == 0) path[i][j] = path[i][j] | 1 << k; } } } int res = dfs(0); System.out.println(res); } } } class PII { double x, y; PII(double x, double y) { this.x = x; this.y = y; } }
狀態轉移DP
// 狀態轉移DP import java.util.*; public class Main { static int N = 20, M = 1 << 18; static PII[] pr = new PII[N]; static int[][] path = new int[N][N]; // f[st]的含義:從0轉移到狀態st的所需的最小步數,只對全1的情況下成立最優解 static int[] f = new int[M]; static int n, m; static void init() { Arrays.fill(f, 0x3f3f3f3f); for (int i = 0; i < N; i ++) Arrays.fill(path[i], 0); } static int cmp(double a, double b) { if (Math.abs(a - b) < 1e-6) return 0; if (a > b) return 1; return -1; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int T = sc.nextInt(); while(T -- > 0) { init(); n = sc.nextInt(); m = sc.nextInt(); for (int i = 0; i < n; i ++) pr[i] = new PII(sc.nextDouble(), sc.nextDouble()); for (int i = 0; i < n; i ++) { path[i][i] = 1 << i; for (int j = 0; j < n; j ++) { double x1 = pr[i].x, y1 = pr[i].y; double x2 = pr[j].x, y2 = pr[j].y; if (cmp(x1, x2) == 0) continue; double a = (y1 / x1 - y2 / x2) / (x1 - x2); if (cmp(a, 0.0) >= 0) continue; double b = (y1 / x1) - (a * x1); for (int k = 0; k < n; k ++) { double x3 = pr[k].x, y3 = pr[k].y; double yy = a * x3 * x3 + b * x3; if (cmp(yy, y3) == 0) path[i][j] = path[i][j] | 1 << k; } } } f[0] = 0; for (int i = 0; i < (1 << n) - 1; i ++) { // 因為是任意選擇一個未被覆蓋的,所以只對全1的情況下成立最優解,其他狀態不保證 int k = -1; for (int j = 0; j < n; j ++) { if ((i & 1 << j) == 0) { k = j; break; } } for (int u = 0; u < n; u ++) { if (f[i] != 0x3f3f3f3f) f[i | path[k][u]] = Math.min(f[i | path[k][u]], f[i] + 1); } } System.out.println(f[(1 << n) - 1]); } } } class PII { double x, y; PII(double x, double y) { this.x = x; this.y = y; } }