Loading

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;
    }
}
posted @ 2022-05-12 14:40  Doubest  阅读(29)  评论(0编辑  收藏  举报