LeetCode 1349. 参加考试的最大学生数,状压DP

news/2024/7/20 22:57:51 标签: 算法, 动态规划, leetcode, c++, 数据结构, 深度优先

一、题目

1、题目描述

给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 '#' 表示;否则,用 '.' 表示。

学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的同时参加考试且无法作弊的 最大 学生人数。

学生必须坐在状况良好的座位上。

2、接口描述

class Solution {
public:
    int maxStudents(vector<vector<char>>& seats) {

    }
};

3、原题链接

1349. 参加考试的最大学生数


二、解题报告

1、思路分析

对于这种网格图上的合法位置问题很容易联想到状压DP

根据题目规则,按行考虑,每一行的有效状态当前行有关也跟上一行有关

状态设计

每一行的状态可以用一个二进制数字来保存,1代表有学生,0代表没有,那么我们定义f[i][j]为下标i行状态为j时最大学生数目

那么递推关系是怎样的呢?

状态转移

首先对于状态j而言,它必须是一个合法状态,他要满足当前行合法即j & (j >> 1) = 0,即无相邻1

那么对于上一行,状态不妨设为t,那么t & (j >> 1 | j << 1) = 0,我们有递推公式

f[i][j] = max(f[i][j] , f[i - 1][t] + num[j]),其中num[j]为状态j中1的个数

有了递推公式之后,其实还是有许多细节要处理的

预处理

根据传入参数,我们可以预处理出每行的座位状态(仍然是1代表有,0代表无),由于每行的有效状态是要从这个座位状态的子集里面挑的,座位状态可以用来枚举子集

状态初始化

我们可以初始化出第0行的所有状态下的最大学生数目,为社么呢?

对于第0行初始化显然问题就简化为了一行位置有若干位置可以坐人,要求不能两两相邻,求最大数目,我们直接贪心的取即可,从第一个空位置开始取,如果相邻也是空位置就跳过去,具体代码就是:

        for(int i = 1 ; i < (1 << n) ; i++)
        {
            int lb = i&-i;
            f[0][i] = f[0][i & ~(lb * 3)] + 1;
        }

2、复杂度

时间复杂度:O(m*3^n) 空间复杂度:O(m*2^n)

3、代码详解

​记忆化搜索版本
class Solution {
public:
int f[8][1<<8] , valid[8];
    int dfs(int x , int y){
        int& res = f[x][y];
        if(~res) return res;
        if(!x)
        {
            if(!y) return res = 0;
            int lb = y&-y;
            return res = dfs(x , (y & ~(lb*3))) + 1;
        }
        res = dfs(x - 1 , valid[x - 1]);
        for(int i = y ; i ; i = (i - 1) & y)
        {
            if(!(i & (i >> 1)))
            {
                int t = valid[x - 1] & ~((i << 1) | (i >> 1));
                res = max(res , dfs(x - 1, t) + __builtin_popcount(i));
            }
        }
        return res;
    }
    int maxStudents(vector<vector<char>>& seats) {
        int m = seats.size() , n = seats[0].size();
        memset(f , -1 , sizeof(f));
        memset(valid , 0 , sizeof(valid));
        for(int i = 0 ; i < m ; i++)
            for(int j = 0 ; j < n ; j++)
                if(seats[i][j] == '.') valid[i] |= (1 << j);
        return dfs(m - 1 , valid[m - 1]);
    }
};

递推版本

class Solution {
public:
int f[8][1<<8] , valid[8];
    int maxStudents(vector<vector<char>>& seats) {
        int m = seats.size() , n = seats[0].size();
        memset(f , 0 , sizeof(f));
        memset(valid , 0 , sizeof(valid));
        for(int i = 0 ; i < m ; i++)
            for(int j = 0 ; j < n ; j++)
                if(seats[i][j] == '.') valid[i] |= (1 << j);
        for(int i = 1 ; i < (1 << n) ; i++)
        {
            int lb = i&-i;
            f[0][i] = f[0][i & ~(lb * 3)] + 1;
        }
        for(int i = 1 ; i < m ; i++)
        {
            for(int j = valid[i] ; j ; j = (j - 1) & valid[i]){
                f[i][j] = f[i - 1][valid[i - 1]];
                for(int k = j ; k ; k = (k - 1) & j)
                {
                    if(!(k & (k >> 1))){
                        int t = valid[i - 1] & ~(k << 1 | k >> 1);
                        f[i][j] = max(f[i][j] , f[i - 1][t] + f[0][k]);
                    }
                }
            }
            f[i][0] = f[i - 1][valid[i - 1]];
        }
        return f[m - 1][valid[m - 1]];
    }
};


http://www.niftyadmin.cn/n/5287450.html

相关文章

21.java的::方法引用的总结。

注意&#xff1a;方法引用返回的是一个函数式接口对象&#xff08;该函数式接口抽象方法被引用方法替代了&#xff0c;调用抽象方法实际上就是调用引用的方法&#xff09;。 比如&#xff1a;Function<String, String> function String::toUpperCase; Java 中的双冒号&…

淄博•关爱天使 质子治疗距普通患者又近一步!质子救助持续发热中

儿童肿瘤近年来有增多趋势&#xff0c;其原因可能有很多&#xff0c;与成人肿瘤一样&#xff0c;儿童肿瘤也分为良性和恶性。当孩子长了良性肿瘤时&#xff0c;开始一般不会有明显的症状&#xff0c;只有在肿瘤长到一定大小&#xff0c;开始挤压周围脏器&#xff0c;并影响这些…

Java_Stream流

一、JDK8新特性&#xff08;Stream流&#xff09; 接下来学习一个全新的知识&#xff0c;叫做Stream流&#xff08;也叫Stream API&#xff09;。它是从JDK8以后才有的一个新特性&#xff0c;是专业用于对集合或者数组进行便捷操作的。有多方便呢&#xff1f;我们用一个案例体…

call、apply、bind区别

问题描述&#xff1a; React死循环问题

异常和智能指针

智能指针的认识 智能指针是一种C语言中用于管理动态内存的工具&#xff0c;它们可以自动管理内存的分配和释放&#xff0c;从而避免内存泄漏和悬空指针等问题。智能指针可以跟踪指向的对象的引用次数&#xff0c;并在需要时自动释放被引用的内存&#xff0c;这极大地提高了内存…

【深度学习-目标检测】01 - R-CNN 论文学习与总结

论文地址&#xff1a;Rich feature hierarchies for accurate object detection and semantic segmentation 论文学习 摘要&#xff08;Abstract&#xff09; 对象检测性能的现状&#xff1a; 在PASCAL VOC数据集上测量的对象检测性能在过去几年已经达到了一个高点。最佳性能…

Plantuml之组件图语法介绍(二十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Protobuf 编码规则及c++使用详解

Protobuf 编码规则及c使用详解 Protobuf 介绍 Protocol Buffers (a.k.a., protobuf) are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data Protocol Buffers&#xff08;简称为protobuf&#xff09;是谷歌的语言无关、…