动态规划-状态压缩-参加考试的最大学生数

2020-02-29 22:18:12

问题描述:

 

问题求解:

解法一:DFS

将原问题转化成图的问题,使用dfs去遍历得到解。

核心思想就是每次遍历到一个点,不仅将其标记为使用,还要将其周围的节点标记为访问过,避免之后遍历到矛盾的节点。

    int res = 0;
    
    public int maxStudents(char[][] seats) {
        List<int[]> pos = new ArrayList<>();
        for (int i = 0; i < seats.length; i++) {
            for (int j = 0; j < seats[0].length; j++) {
                if (seats[i][j] == '.') pos.add(new int[]{i, j});
            }
        }
        for (int[] start : pos) {
            Set<int[]> used = new HashSet<>();
            res = Math.max(res, dfs(pos, start, used));
        }
        return res;
    }
    
    private int dfs(List<int[]> pos, int[] start, Set<int[]> used) {
        if (used.contains(start)) return 0;
        used.add(start);
        for (int[] next : pos) {
            if (check(next, start) || check(start, next)) used.add(next);
        }
        int res = 1;
        for (int[] next : pos) {
            if (used.contains(next)) continue;
            res += dfs(pos, next, used);
        }
        return res;
    }
    
    private boolean check(int[] p1, int[] p2) {
        if (p1[0] == p2[0] && p1[1] == p2[1] - 1) return true;
        if (p1[0] == p2[0] && p1[1] == p2[1] + 1) return true;
        if (p1[0] == p2[0] - 1 && p1[1] == p2[1] - 1) return true;
        if (p1[0] == p2[0] - 1 && p1[1] == p2[1] + 1) return true;
        return false;
    }

 

解法二:DP

本题最优的解法是DP,采取的思路是当前行的状态只和上一行相关,因此如果我们遍历上下行的所有状态,从中挑选出符合条件的状态,就可以得到解。

本题的思路和开关灯泡的搜索解有异曲同工的地方。

    public int maxStudents(char[][] seats) {
        int m = seats.length;
        int n = seats[0].length;
        int[] broken = new int[m + 1];
        for (int i = 0; i < m; i++) {
            int state = 0;
            for (int j = 0; j < n; j++) {
                if (seats[i][j] == '#') state |= (1 << j);
            }
            broken[i + 1] = state;
        }
        int[][] dp = new int[m + 1][1 << n];
        for (int i = 1; i <= m; i++) {
            for (int u = 0; u < (1 << n); u++) {
                if ((u & (u >> 1)) != 0 || (u & broken[i - 1]) != 0) continue;
                for (int v = 0; v < (1 << n); v++) {
                    if ((v & (v >> 1)) != 0 || (v & broken[i]) != 0 ||(v & (u >> 1)) != 0 || (v & (u << 1)) != 0) continue;
                    dp[i][v] = Math.max(dp[i][v], dp[i - 1][u] + helper(v));
                }
            }
        }
        int res = 0;
        for (int i = 0; i < (1 << n); i++) res = Math.max(res, dp[m][i]);
        return res;
    }
    
    private int helper(int num) {
        int res = 0;
        for (int i = 0; i < 32; i++) if ((num & (1 << i)) != 0) res += 1;
        return res;
    }

 

解法三:DP + 子集枚举

    public int maxStudents(char[][] seats) {
        int m = seats.length;
        int n = seats[0].length;
        int[] states = new int[m + 1];
        for (int i = 0; i < m; i++) {
            int state = 0;
            for (int j = 0; j < n; j++) {
                if (seats[i][j] == '.') state |= (1 << j);
            }
            states[i + 1] = state;
        }
        int[][] dp = new int[m + 1][1 << n];
        for (int i = 1; i <= m; i++) {
            for (int u = states[i - 1];; u = (u - 1) & states[i - 1]) {
                for (int v = states[i];; v = (v - 1) & states[i]) {
                    if ((u & (u >> 1)) == 0 && (v & (v >> 1)) == 0 && (v & (u >> 1)) == 0 && (v & (u << 1)) == 0) dp[i][v] = Math.max(dp[i][v], dp[i - 1][u] + helper(v));
                    if (v == 0) break;
                }
                if (u == 0) break;
            }
        }
        int res = 0;
        for (int i = states[m];; i = (i - 1) & states[m]) {
            res = Math.max(res, dp[m][i]);
            if (i == 0) break;
        }
        return res;
    }
    
    private int helper(int num) {
        int res = 0;
        for (int i = 0; i < 32; i++) if ((num & (1 << i)) != 0) res += 1;
        return res;
    }

  

 

  

 

posted @ 2020-02-29 22:26  hyserendipity  阅读(231)  评论(0编辑  收藏  举报