LeeCode 1349 最大孤立点集 / 状态压缩 DP

159 篇文章 1 订阅
题意

传送门 LeeCode 1349. 参加考试的最大学生数

题解
最大孤立点集

题意即求学生互不位于可作弊位置的最大人数,将学生看做顶点,可作弊的座位间连一条边,则问题转化为求最大孤立点集。对于二分图而言(可作弊位置间 y y y 坐标总是不相同的,所有的边都连接 y y y 为偶数和 y y y 为奇数的顶点),最大独立集等于最大二分匹配。

class Solution {
public:
    static const int MAX_V = 64;
    int dx[4] = {-1, 0, -1, 0};
    int dy[4] = {-1, -1, 1, 1};
    int V, m, n;
    vector<int> G[MAX_V];
    int match[MAX_V];
    bool used[MAX_V];

    void add_edge(int u, int v){
    	G[u].push_back(v);
    	G[v].push_back(u);
    }

    bool dfs(int v){
	    used[v] = true;
	    for(int i = 0; i < G[v].size(); i++){
		    int u = G[v][i], w = match[u];
		    if(w < 0 || (!used[w] && dfs(w))){
			    match[v] = u;
			    match[u] = v;
			    return true;
		    }
	    }
	    return false;
    }
	// 最大二分匹配
    int bipartite_matching(){
	    int res = 0;
	    memset(match, -1, sizeof(match));
	    for(int v = 0; v < V; v++){
		    if(match[v] < 0){
			    memset(used, 0, sizeof(used));
			    if(dfs(v)){
				    ++res;
			    }
		    }
	    }
	    return res;
    }

    void clear_graph(){
	    for(int v = 0; v < V; v++) G[v].clear();
    }
    
    int maxStudents(vector<vector<char>>& seats) {
        clear_graph();
        m = seats.size(), n = seats[0].size();
        V = n * m;	// 顶点值范围
        int num = 0;	// 可座位置数
        for(int x = 0; x < m; x++){
            for(int y = 0; y < n; y++){
                if(seats[x][y] == '.'){
                    ++num;
                    for(int i = 0; i < 4; i++){
                        int nx = x + dx[i], ny = y + dy[i];
                        if(nx >= 0 && nx < m && ny >= 0 && ny < n && seats[nx][ny] == '.'){
                            add_edge(x * n + y, nx * n + ny);
                        }
                    }
                }
            }
        }
        return num - bipartite_matching();
    }
};
状态压缩 DP

压缩每一行的状态,通过 s & ( s > > 1 ) s\&(s>>1) s&(s>>1) 判断当前行状态是否合法(即是否有学生处于相邻位置),通过 ( s 1 & ( s 2 > > 1 ) )   ∣ ∣   ( s 1 & ( s 2 < < 1 ) ) (s1\&(s2>>1))\ ||\ (s1\&(s2<<1)) (s1&(s2>>1))  (s1&(s2<<1)) 判断前一行状态是否合法(即前一行是否有学生位于当前行学生左上、右上位置)。 d p [ i ] [ s ] dp[i][s] dp[i][s] 代表第 i i i 行状态为 s s s 时, 0 0 0 i i i 行的最大学生数。

class Solution {
public:
    int n, m;
    int mat[8];
    int dp[8][1 << 8];
    int sum(int x){
        int s = 0;
        while(x > 0) s += 1 & x, x >>= 1;
        return s;
    }
    bool judge(int s1, int s2){
        return (s1 & (s2 << 1)) || (s1 & (s2 >> 1));
    }
    int maxStudents(vector<vector<char>>& seats) {
        m = seats.size(), n = seats[0].size();
        for(int i = 0; i < m; i++){
            int s = 0;
            for(int j = 0; j < n; j++){
                if(seats[i][j] == '#') s |= 1 << j;
            }
            mat[i] = s;
        }

        memset(dp, 0, sizeof(dp));
        for(int s = 0; s < 1 << n; s++){
            if(!(s & mat[0] || s & (s >> 1))) dp[0][s] = sum(s);
        }
        for(int i = 1 ; i < m; i++){
            for(int s1 = 0; s1 < 1 << n; s1++){
                if(s1 & mat[i] || (s1 & (s1 >> 1))) continue;
                for(int s2 = 0; s2 < 1 << n; s2++){
                    if(!judge(s1, s2)) dp[i][s1] = max(dp[i][s1], dp[i - 1][s2]);
                }
                dp[i][s1] += sum(s1);
            }
        }
        return *max_element(dp[m-1], dp[m-1]+(1 << n));
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值