【算法提高:动态规划】1.5 状态压缩DP TODO

news2024/12/23 6:10:57

文章目录

  • 状态压缩DP
  • 例题列表
    • 棋盘式
      • 1064. 小国王⭐🐂(好题!)
        • 做题套路总结
      • 327. 玉米田(好题!🐂 和1064. 小国王差不多的题目)
      • 292. 炮兵阵地(和上面两道题差不多,需要多考虑上上一行)
    • 集合
      • 524. 愤怒的小鸟🚹🚹🚹(TODO)
      • 529. 宝藏🚹🚹🚹🚹🚹(TODO)
  • 相关链接

状态压缩DP

状压 DP 是动态规划的一种,通过将状态压缩为整数来达到优化转移的目的。
在这里插入图片描述
更多介绍可见:相关链接 部分。

例题列表

棋盘式

1064. 小国王⭐🐂(好题!)

https://www.acwing.com/activity/content/problem/content/1292/

在这里插入图片描述

洛谷相同题目链接:P1896 [SCOI2005] 互不侵犯

每个国王会攻击周围一圈儿。

每一层只需要考虑上一层的状态就可以了。


dp数组定义:long[][][] dp = new long[N][K][M]; 第i行,已经放了j个国王,当前行的国王集合为a,此时的方案数。

枚举顺序见代码。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    final static int N = 12, M = 1 << 10, K = 110;      // 可以开大一点无所谓

    static int n, m;
    static List<Integer> state = new ArrayList<>();     // 里面存放的是在一行中没有冲突的集合
    static int[] cnt = new int[M];
    static List<Integer>[] head = new ArrayList[M];     // 里面存放的是每一种状态相邻的行可以放置哪些状态
    static long[][][] dp = new long[N][K][M];
    static {
        Arrays.setAll(head, e -> new ArrayList<>());
    }

    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        n = sin.nextInt();
        m = sin.nextInt();

        // 预先处理出所有可选的状态集合(即在一行内没有冲突的集合),以及这种状态的行中有几个国王
        for (int i = 0; i < 1 << n; ++i) {
            if (check(i)) {
                state.add(i);
                cnt[i] = count(i);
            }
        }

        // 预先处理每一行之后 相邻的行可以放置哪种状态集合
        for (int i = 0; i < state.size(); ++i) {
            for (int j = 0; j < state.size(); ++j) {
                int a = state.get(i), b = state.get(j);
                // 如果没有冲突
                // 没有列上一样的,也没有列上相邻的
                if ((a & b) == 0 && check(a | b)) {
                    head[i].add(j);
                }
            }
        }

        // 第i行,已经放了j个国王,当前行的国王集合为a
        dp[0][0][0] = 1;

        // 枚举每一行
        for (int i = 1; i <= n + 1; i++) {
            // 枚举已经选了m个国王(需要从0枚举到m)
            for (int j = 0; j <= m; ++j) {
                // 枚举每一种状态作为当前行(不管是不是合法的,无所谓,因为不合法的对应的head[a]里面是空的)
                for (int a = 0; a < state.size(); ++a) {
                    // 枚举a后面可以跟着的每一种b
                    for (int b: head[a]) {
                        int c = cnt[state.get(a)];
                        // 如果当前已经选择的国王数量>=当前行的国王数量
                        if (j >= c) {
                            dp[i][j][a] += dp[i - 1][j - c][b];
                        }
                    }
                }
            }
        }
        System.out.println(dp[n + 1][m][0]);
    }

    // 检查一个状态集合是否在这一行内有冲突
    static boolean check(int state) {
        for (int i = 0; i < n; ++i) {
            // 如果连续两列都为1,表示有冲突
            if (((state >> i & 1) == 1 && (state >> i + 1 & 1) == 1)) return false;
        }
        return true;
    }

    // 计算这个状态集合中,摆放了几个国王
    static int count(int state) {
        int res = 0;
        for (int i = 0; i < n; ++i) res += state >> i & 1;
        return res;
    }
}

做题套路总结

最后来总结一下写法:

  1. 先预先处理出,所有在一行中没有冲突的列集合
  2. 然后对计算出对所有合理列集合 来说 没有冲突的列集合
  3. 最后 枚举行、当前行的列状态、上一行的列状态,计算状态转移

327. 玉米田(好题!🐂 和1064. 小国王差不多的题目)

https://www.acwing.com/problem/content/329/

在这里插入图片描述

跟上一题相比有一些变化:

  • 冲突的范围从周围一圈变成十字的形状
  • 部分格子是无法放置玉米的
  • 种植玉米的数量是没有限制的
import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    final static int N = 14;
    final static long MOD = (long)1e8;
    static int m, n;
    static int[] land = new int[N];         // 记录每一行中的哪些列是不能种植的
    static List<Integer> states = new ArrayList<>();        // 记录所有在一行中合法的状态
    static List<Integer>[] head = new ArrayList[1 << N];
    static {
        Arrays.setAll(head, e -> new ArrayList<>());
    }

    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        m = sin.nextInt();
        n = sin.nextInt();
        for (int i = 1; i <= m; ++i) {
            for (int j = 0; j < n; ++j) {
                int t = sin.nextInt();
                land[i] += (t ^ 1) << j;      // 0表示不育
            }
        }

        // 预先处理出在一行中合法的所有状态,加入states中
        for (int i = 0; i < 1 << n; ++i) {
            if (check(i)) states.add(i);
        }

        // 预先处理出哪两种行可以放在一起
        for (int i = 0; i < states.size(); ++i) {
            for (int j = 0; j < states.size(); ++j) {
                int a = states.get(i), b = states.get(j);
                // 只要这两行在列上没有重复,就可以放在一起
                if ((a & b) == 0) {
                    head[i].add(j);
                }
            }
        }

        long[][] dp = new long[m + 2][states.size() + 1];
        dp[0][0] = 1;       // dp数组初始化
        // 枚举每一行
        for (int i = 1; i <= m + 1; ++i) {
            // 枚举该行的状态
            for (int j = 0; j < states.size(); ++j) {
                if ((states.get(j) & land[i]) == 0) {
                    // 枚举可以从这种行转移过来的行
                    for (int k: head[j]) {
                        dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD;
                    }
                }
            }
        }

        System.out.println(dp[m + 1][0]);
    }

    static boolean check(int state) {
        for (int i = 0; i < n - 1; ++i) {
            // 检查是否有连续两位都是1的情况
            if ((state >> i & 1) == 1 && ((state >> i + 1 & 1) == 1)) return false;
        }
        return true;
    }
}

Q:为什么 dp[][] 数组初始化时只 设置 dp[0][0] = 1,而不把 所有 dp[0][j] = 1?
A:dp[0][0] = 1表示在还没有种植任何玉米的状态下(也就是前0行),这种情况只有1种。而dp[0][j] (j != 0)的含义是,在还没有种植任何玉米的状态下,最后一行(也就是不存在的这一行)的种植状态是states[j]的种植方式数量,这显然是不可能的,所以dp[0][j] (j != 0)都应该为0。

292. 炮兵阵地(和上面两道题差不多,需要多考虑上上一行)

https://www.acwing.com/problem/content/294/

在这里插入图片描述

跟上面两道题目相比,多考虑上上一行就好了。
dp 递推的过程也 多写一层循环。

import java.io.BufferedInputStream;
import java.util.*;

public class Main {
    final static int N = 105, M = 11;
    static int n, m;
    static int[] land = new int[N], cnt = new int[1 << M];         // 记录每一行,不能放置炮兵的列集合
    static List<Integer> states = new ArrayList<>();    // 记录所有在一行中合理的列集合
    static List<Integer>[] head = new ArrayList[1 << M];
    static {
        Arrays.setAll(head, e -> new ArrayList<>());
    }

    public static void main(String[] args) {
        Scanner sin = new Scanner(new BufferedInputStream(System.in));
        n = sin.nextInt();
        m = sin.nextInt();

        // 记录每一行不能被放置的列集合
        for (int i = 1; i <= n; ++i) {
            String s = sin.next();
            for (int j = 0; j < m; ++j) {
                land[i] += (s.charAt(j) == 'H'? 1: 0) << j;
            }
        }

        // 计算出所有在一行中合理的列集合
        for (int j = 0; j < 1 << m; ++j) {
            if (check(j)) {
                states.add(j);
                cnt[j] = count(j);
            }
        }

        // 计算出各个集合可以和哪些集合相邻
        int l = states.size();
        for (int i = 0; i < l; ++i) {
            for (int j = 0; j < l; ++j) {
                if ((states.get(i) & states.get(j)) == 0) {
                    head[i].add(j);
                }
            }
        }

        int[][][] dp = new int[n + 1][l][l];
        // 枚举每一行
        for (int r = 1; r <= n; ++r) {
            // 枚举当前的每个集合
            for (int i = 0; i < l; ++i) {
                if ((states.get(i) & land[r]) == 0) {
                    // 枚举上一个集合
                    for (int j: head[i]) {
                        // 枚举上上个集合
                        for (int k: head[j]) {
                            if ((states.get(i) & states.get(k)) == 0) {
                                dp[r][j][i] = Math.max(dp[r - 1][k][j] + cnt[states.get(i)], dp[r][j][i]);
                            }
                        }
                    }
                }

            }
        }
        int ans = 0;
        for (int i = 0; i < l; ++i) {
            for (int j = 0; j < l; ++j) {
                ans = Math.max(ans, dp[n][i][j]);
            }
        }
        System.out.println(ans);
    }

    private static boolean check(int state) {
        for (int i = 0; i < m; ++i) {       // 这里最好写成 i < m,写成 i < m - 2 时答案就不对了
            // 邻近两个范围内不能有炮兵
            if ((state >> i & 1) == 1 && ((state >> i + 1 & 1) == 1 || (state >> i + 2 & 1) == 1)) return false;
        }
        return true;
    }

    // 计算这种集合中有多少个炮兵
    static int count(int state) {
        int res = 0;
        for (int i = 0; i < m; ++i) res += state >> i & 1;
        return res;
    }
}

集合

524. 愤怒的小鸟🚹🚹🚹(TODO)

https://www.acwing.com/problem/content/526/
在这里插入图片描述
在这里插入图片描述
数据范围
在这里插入图片描述

在这里插入代码片

529. 宝藏🚹🚹🚹🚹🚹(TODO)

https://www.acwing.com/problem/content/531/
在这里插入图片描述

在这里插入代码片

相关链接

从集合论到位运算——常见位运算技巧及相关习题 & 状态压缩DP
【力扣周赛】第 355 场周赛(构造&二分答案&异或前缀 状态压缩⭐)
【算法基础:动态规划】5.4 状态压缩DP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/825412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

股票量化系统QTYX选股框架实战案例集|地产看前排承接做后排补涨,砸涨停板吃8点小肉-230801...

前言 “实战案例个股画像”系列和大家分享我基于QTYX选股框架&#xff0c;在实战中选股的案例&#xff0c;和大家一起见证QTYX选股框架逐步完善的过程&#xff0c;帮助大家理解QTYX的精髓。 关于QTYX的使用攻略可以查看链接&#xff1a;QTYX使用攻略 关于QTYX初衷和精髓可以查看…

CSS图片放到<div>里面,自适应宽高全部显示,点击图片跳到新页面预览,点击旋转按钮图片可旋转

有一个需求是图片放到一个固定宽高的<div>里面&#xff0c;不管是横图还是竖图&#xff0c;都要全部显示出来并且保持图片的长宽比例不变形&#xff0c;点击图片可以跳到一个新页面预览&#xff0c;代码如下&#xff1a; <!DOCTYPE html> <html> <head>…

图解系列 DNS查找过程和DNS缓存

DNS 充当地址簿。它将人类可读的域名 (google.com) 转换为机器可读的 IP 地址 (142.251.46.238)。 开局一张图 来自&#xff1a;https://xiaolishen.medium.com/the-dns-lookup-journey-240e9a5d345c 寻址流程 查询浏览器缓存&#xff1a;当你输入一个域名后&#xff0c;浏览…

VS创建QT项目的几个注意点

前提是已经安装好了VS、QT和Qt VS Tool。 一、创建项目无法找到头文件&#xff0c;点击编译运行又可以执行。虽然可以编译运行&#xff0c;但是无法找不到头文件显示是红色并且在开发时无法出现相关代码提示&#xff0c;并且无法导入QT相关头文件。 解决方法&#xff1a; 1、…

Android多线程编程、异步消息处理机制以及new Handler()被标记为过时的解决办法,解决Handler内存泄漏问题和AsyncTask的基本用法

一、Android多线程编程 1、异步消息处理机制 1.1 弱引用 WeakReference&#xff08;弱引用&#xff09;是一种在Java中用于管理对象的引用的特殊引用类型。它的作用是在垃圾回收过程中&#xff0c;允许对象在没有强引用指向它时被回收&#xff08;当一个对象只有弱引用指向它…

如何防止亚马逊买家号关联?

防止亚马逊买家号关联是指避免在同一家亚马逊账户下使用多个买家号。有时&#xff0c;卖家或买家会创建多个买家号来规避亚马逊的规则和限制。然而&#xff0c;这样的行为违反了亚马逊的政策&#xff0c;并可能导致账户被封禁或其他严重的后果。 而想要防关联&#xff0c;可以从…

【Linux】IO 篇:文件调用原理,文件描述符,FILE的内涵,解析重定向,理解缓冲区

文章目录 一、系统调用接口二、文件调用1. 文件描述符 fd2. 文件调用原理3. FILE 三、重定向dup2 四、缓冲区简易 FILE 的代码实现 文件被加载之前&#xff0c;被存在磁盘上&#xff0c;操作文件&#xff0c;文件的部分内容则会被调度到 内存中。 要分析文件&#xff0c;我们也…

问道管理:沪指震荡跌0.84%,银行、医药等板块走弱,地产板块逆市拉升

2日早盘&#xff0c;沪指盘中震动下探&#xff0c;深成指、创业板指亦走低&#xff1b;两市半日成交约5400亿元&#xff0c;北向资金净卖出约35亿元。 到午间收盘&#xff0c;沪指跌0.84%报3263.2点&#xff0c;深成指跌0.42%&#xff0c;创业板指跌0.27%&#xff0c;上证50指数…

UPnP是什么?有什么更好的连接方案?快解析内网穿透

一、UPnP是什么 有些小伙伴对于UPnP并不了解&#xff0c;其实UPnP只是一种网络协议&#xff0c;主要作用就是简化家庭和企业网络中设备之间的连接和通信过程&#xff0c;它的主要目标是实现网络的无缝连接&#xff0c;并简化相关网络操作。 二、UPnP有什么主要作用&#xff1…

激光切割机好不好?激光切割与线切割应该怎么选择对比

在选择激光切割机与线切割机进行比较时&#xff0c;我们首先需要理解两者的特性与特点。 激光切割机&#xff1a;现阶段主流的激光切割设备主要包括光纤激光切割机和CO2激光切割机。其中CO2激光切割机主要用于切割厚板&#xff0c;但也可完成非金属材料的切割。光纤激光切割机则…

面试题:JS中的String常见方法有哪些?

面试题&#xff1a;说不出五个就尴尬了&#xff01;我目前只写了几个方法&#xff0c;待更新中。。。 1、length2、slice()3、substr()4、substring()5、split()6、indexOf() 1、length 作用&#xff1a;检测字符串的长度。 let str abcde console.log(str.length) // 52、sl…

深入解读Gartner 2023中国数据、分析与AI技术成熟度曲线报告

近日&#xff0c;国际权威研究机构Gartner发布了《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff08;2023中国数据、分析与AI技术成熟度曲线报告&#xff09;。作为业内的权威报告&#xff0c; Gartner 每年针对技术、应用和行业创建的技术成熟度曲线&#…

【WebRTC---源码篇】(二:一)PeerConnection详解

Track的添加 上图是整体流程图 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::AddTrack(rtc::scoped_refptr<MediaStreamTrackInterface> track,const std::vector<std::string>& stream_ids) {RTC_DCHECK_RUN_ON(signal…

Linux(三)---------网络路由命令(route路由命令)

一.route路由命令 1.什么是route路由&#xff1f; 计算机之间的数据传输必须经过网络&#xff0c;网络可以直接两台计算机&#xff0c;也可以通过一个一个的节点去连接。路由可以理解为互联网的中转站&#xff0c;网络中的数据包就是通过一个一个的路由器转发到目的地的。 路…

【枚举边+树的直径】CF14D

Problem - 14D - Codeforces 题意&#xff1a; 思路&#xff1a; 两条链不相交&#xff0c;说明是在不同连通分量中&#xff0c;我们可以枚举边来把树分为两个连通分量&#xff0c;然后分别计算直径即可 Code&#xff1a; #include <bits/stdc.h>#define int long lo…

目标检测与跟踪 (1)- 机器人视觉与YOLO V8

目录 1、研究背景 2. 算法原理及对比 2.1 点对特征&#xff08;Point Pairs&#xff09; 2.2 模板匹配 2.3 霍夫森林 2.4 深度学习 3、YOLO家族模型演变 4、YOLO V8 1、研究背景 机器人视觉识别技术是移动机器人平台十分关键的技术&#xff0c;代表着机器人智能化、自动化…

XGBoost的参数空间与超参数优化

目录 1. 确定XGBoost的参数空间 2. 基于TEP对XGBoost进行优化 1. 确定XGBoost的参数空间 对任意集成算法进行超参数优化之前&#xff0c;我们需要明确两个基本事实&#xff1a;①不同参数对算法结果的影响力大小&#xff1b;②确定用于搜索的参数空间。对XGBoost来说&#x…

什么?仅使用配合功能就能让内燃机运动起来

配合作为SOLIDWORKS中装配体的重要命令&#xff0c;拥有着非常强大的功能。根据应用场合的不同软件将其分类为标准配合、高级配合和机械配合&#xff0c;按照这个顺序功能也越来越强大。本次使用内燃机的模型&#xff0c;将三个配合依次进行展示。 标准配合包含了常见的自由度约…

redis安装,开启自启动(Windows)

1、下载redis包 Releases tporadowski/redis GitHub 2、配置环境变量 path中添加redis解压后的路径&#xff1b; 3、配置文件修改 注释掉 bind 127.0.0.1 使其他ip可以远程访问&#xff1b; 修改redis密码 4、启动Redis服务 redis-server.exe --service-install redis.w…

如何让win10开机默认开启小键盘?

一、背景 省流版&#xff1a;直接看最后。 如题。 随便搜一搜&#xff0c;就会发现&#xff0c;有大量雷同&#xff0c;甚至内容完全相同的文章&#xff0c;然而并无卵用。 如&#xff1a; 如何让win10开机默认开启小键盘&#xff1f;【知乎】 如何让win10开机默认开启小键盘…