1. 程式人生 > 其它 >AcWing 524. 憤怒的小鳥

AcWing 524. 憤怒的小鳥

題目連結:

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;
    }
}