状态压缩的三种模型

第一种类型(摆放方块):

 

代码如下:

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
using namespace std;
using ll = long long;
const int N = 15, M = 1 << N; 
#define x first
#define y second
typedef pair<int, int> PII;
ll f[N][M], n, m;
bool st[M];
vector<int> state[M]; // 二维数组记录合法的状态


int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    while (cin >> n >> m, n || m) 
    { //读入n和m,并且不是两个0即合法输入就继续读入
        
        //第一部分:预处理1
        //对于每种状态,先预处理每列不能有奇数个连续的0
        
        for(int i = 0; i < (1 << n); i ++) 
        {
            
            int cnt = 0 ;//记录连续的0的个数
            
            bool isValid = true; // 某种状态没有奇数个连续的0则标记为true
            
            for(int j = 0; j < n; j ++) 
            { //遍历这一列,从上到下,一列有n个数
                
                if ( (i >> j) & 1) 
                {  
                    //i >> j位运算,表示i(i在此处是一种状态)的二进制数的第j位; 
                    // &1为判断该位是否为1,如果为1进入if
                    if (cnt & 1) // 二进制第0位是1就是奇数
                    { 
                        //这一位为1,看前面连续的0的个数,如果是奇数(cnt &1为真)则该状态不合法
                        isValid = false; 
                        break;
                    } 
                    
                    cnt = 0; // 既然该位是1,并且前面不是奇数个0(经过上面的if判断),计数器清零。
                    //其实清不清零没有影响
                }
                else cnt ++; //否则的话该位还是0,则统计连续0的计数器++。
            }
            if (cnt & 1)  isValid = false; // 最下面的那一段判断一下连续的0的个数,也就是说最下面有一段连续的0
            
            st[i]  = isValid; //状态i是否有奇数个连续的0的情况,输入到数组st中
        }
        
        // 第二部分:预处理2
        // 经过上面每种状态 连续0的判断,已经筛掉一些状态。
        //下面来看进一步的判断:看第i-2列伸出来的和第i-1列伸出去的是否冲突
        
        for (int j = 0; j < (1 << n); j ++) 
        { // 对于第i列的所有状态
            state[j].clear(); // 清空上次操作遗留的状态,防止影响本次状态。
            
            for (int k = 0; k < (1 << n); k ++) 
            { // 对于第i-1列所有状态
                if ((j & k) == 0 && st[j | k]) 
                    // 第i-2列伸出来的 和第i-1列伸出来的不冲突(不在同一行) 
                    // 解释一下st[j | k] 
                    // 已经知道st[]数组表示的是这一列没有连续奇数个0的情况,
                    // 我们要考虑的是第i-1列(第i-1列是这里的主体)中从第i-2列横插过来的,
                    // 还要考虑自己这一列(i-1列)横插到第i列的
                    // 比如 第i-2列插过来的是k=10101,第i-1列插出去到第i列的是 j =01000,
                    // 那么合在第i-1列,到底有多少个1呢?
                    // 自然想到的就是这两个操作共同的结果:两个状态或。 j | k = 01000 | 10101 = 11101
                    // 这个 j|k 就是当前 第i-1列的到底有几个1,即哪几行是横着放格子的
                    
                    state[j].push_back(k); 
                //二维数组state[j]表示第j行, 
                //j表示 第i列“真正”可行的状态,
                //如果第i-1列的状态k和j不冲突则压入state数组中的第j行。
                //“真正”可行是指:既没有前后两列伸进伸出的冲突;又没有连续奇数个0。
            }
            
        }
        
        //第三部分:dp开始
        
        memset(f, 0, sizeof f);  
        //全部初始化为0,因为是连续读入,这里是一个清空操作。
        //类似上面的state[j].clear()
        
        f[0][0] = 1 ;// 这里需要回忆状态表示的定义
        //按定义这里是:前第-1列都摆好,且从-1列到第0列伸出来的状态为0的方案数。
        //首先,这里没有-1列,最少也是0列。
        //其次,没有伸出来,即没有横着摆的。即这里第0列只有竖着摆这1种状态。
        
        for (int i = 1; i <= m; i ++) { //遍历每一列:第i列合法范围是(0~m-1列)
            for (int j = 0; j < (1<<n); j ++) {  //遍历当前列(第i列)所有状态j
                for (auto k : state[j])    // 遍历第i-1列的状态k,如果“真正”可行,就转移
                    f[i][j] += f[i-1][k];    // 当前列的方案数就等于之前的第i-1列所有状态k的累加。
            }
        }
        
        //最后答案是什么呢?
        //f[m][0]表示 前m-1列都处理完,并且第m-1列没有伸出来的所有方案数。
        //即整个棋盘处理完的方案数
        
        cout << f[m][0] << endl;
    }
    
    return 0;
}

第二种类型(覆盖点):

代码如下:

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
using namespace std;
using ll = long long;
const int N = 20, M = 1 << N; 
#define x first
#define y second
typedef pair<int, int> PII;
ll f[M][N], n, m, weight[N][N];
bool st[M];
// 核心:
// 1. 哪些点被用过
// 2. 目前停在哪个点上
// 不同的状态数:2^20(20个点,用或不用,一维) * 20(二维) = 2 * 10^7
// f[state][j] = f[state_k][k] + weight[k][j],state_k = state去掉j之后的集合,state_k要包含k



int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    cin >> n;
    
    for (int i = 0; i < n ; ++i)
        for (int j = 0; j < n; ++j)
            cin >> weight[i][j];
    
    memset(f, 0x3f, sizeof f);
    f[1][0] = 0;// 初始化,初始状态state为1(第一个点),目前停在0这个点上,走的路程为0
    
    for (int i = 0; i < 1 << n ; ++i)
        for (int j = 0; j < n; ++j)
        {
            if (i >> j & 1) // 判断是否合法
            {
                for (int k = 0; k < n; ++k)
                {
                    //当前状态是i,想算k状态
                    if ((i - (1 << j)) >> k & 1)// 当前状态将第j位减去,因为k还没有走到j,最后判断合法性
                    {
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + weight[k][j]);
                    }
                }
            }
        }
    
    cout << f[(1 << n) - 1][n - 1];
    return 0;
}

第三种类型(枚举合法方案数):

#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
#include<tuple>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 110, mod = 1000000007, INF = 0x3f3f3f3f, M = 1 << 6, P = 13331, K = 21;
const double eps = 1e-8;
#define x first
#define y second
typedef pair<int, int> PII;
int T, n, m, k, len, res, e[M], ne[M], h[N], w[M], idx, dist[N], f[2][M][M][K], a[N];
bool st[M], flag;
int months[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
//int dx[9] = {-1, -1, -1, 0, 0, 1, 1, 1, 0}, dy[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
//priority_queue<PII, vector<PII>, greater<PII>> q[N];
vector<int> state;
ll cnt[M];
vector<int> head[M];

int get_count(int x) {
    int res = 0;

    while (x) {
        ++res;
        x -= x & -x;
    }
    return res;
}


bool check1(int a, int b)
{
    return !(b & (a >> 2) || a & (b >> 2));
}

bool check2(int a, int b)
{
   return !(b & (a >> 1) || a & (b >> 1));
}


int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

    cin >> n >> m >> k;
    // 枚举同一行的合法状态
    for (int st = 0; st < 1 << n; ++st) {
        state.push_back(st), cnt[st] = get_count(st);
    }

    // 枚举相邻的两行的合法状态
    for (auto st : state) {
        for (auto ne_st: state) {
            if (check1(st, ne_st)) {
                head[st].push_back(ne_st);
            }
        }
    }

    // 初始化
    // 考虑前i层,第i层状态为st 第i-1状态为p1 前i层放置了j个国王
    f[0][0][0][0] = 1;

    for (int i = 1; i <= m + 2; ++i) {
        for (int j = 0; j <= k; ++j) {
            for (auto &st : state) {
                for (auto &p1 : head[st]) {
                        // 清空
                        // 这里就不需要保证第i行和i-1行是否合法了,因为取出的元素已经保证合法
                        f[i & 1][st][p1][j] = 0;
                    for (auto &p2 : head[p1]) {
                        // 保证第i-2行合法
                        if (check2(st, p2)) {
                             if (j - cnt[st] >= 0) {
                                f[i & 1][st][p1][j] = (f[i & 1][st][p1][j] + f[i - 1 & 1][p1][p2][j - cnt[st]]) % mod;
                            }
                        }
                    }
                }
            }
        }
    }

    cout << f[m + 2 & 1][0][0][k] % mod;

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3+ts+element home页面侧边栏+头部组件+路由组件组合页面教程

文章目录 效果展示template代码script代码样式代码 效果展示 template代码 <template><el-container class"home"><el-aside class"flex" :style"{ width: asideDisplay ? 70px : 290px }"><div class"aside-left&q…

项目中自动引入神器 - unplugin-auto-import/unplugin-vue-components

前端 项目中 自动引入 神器 前言 在开发中&#xff0c;我们总喜欢站在巨人的肩膀上开发&#xff0c;比如用一些 框架&#xff1a;vue,react, 组件库&#xff1a;element&#xff0c;ant。 工具函数&#xff1a;axios&#xff0c;lodash 现在是模块化时代&#xff0c;我们…

基于SpringBoot后端实现连接MySQL数据库并存贮数据

目录 一、什么是MySQL数据库 二、基于SpringBoot框架连接MySQL数据库 1、首先添加MySQL依赖&#xff1a; 2、配置数据库连接&#xff1a; 3、创建实体类&#xff1a; 4、创建Repository接口&#xff1a; 5、使用Repository&#xff1a; 三、编写业务SQL语句 1、使用Spring Data…

浅模仿小米商城布局(有微调)

CSS文件 *{margin: 0;padding: 0;box-sizing: border-box; }div[class^"h"]{height: 40px; } div[class^"s"]{height: 100px; } .h1{width: 1528px;background-color: green; } .h11{background-color:rgb(8, 220, 8); } .h111{width: 683px;background-c…

异或和之和【蓝桥杯】/拆位+贡献法

异或和之和 拆位贡献法 思路&#xff1a;刚开始考虑遍历L和R&#xff0c;同时可以用一个二维数组存储算过的值&#xff0c;但是时间复杂度还是O(n^2)&#xff0c;所以这种还是要拆位和利用贡献法 可以对于每个位&#xff0c;一定区间内&#xff0c;如果有奇数个1则异或值为1&…

阿里云ECS选型推荐配置

本文介绍构建Kubernetes集群时该如何选择ECS类型以及选型的注意事项。 集群规格规划 目前在创建Kubernetes集群时&#xff0c;存在着使用很多小规格ECS的现象&#xff0c;这样做有以下弊端&#xff1a; 网络问题&#xff1a;小规格Worker ECS的网络资源受限。 容量问题&…

Cubase 8.0 下载地址及安装教程

Cubase是一款流行的音乐制作和音频录制软件&#xff0c;由德国公司Steinberg开发。它是一款专业级的数字音频工作站&#xff08;DAW&#xff09;&#xff0c;广泛应用于音乐制作、音频录制、混音和制作等领域。 Cubase提供了丰富的功能和工具&#xff0c;用于录制、编辑、混音…

ubuntu22.04物理机双系统手动分区

ubuntu22.04物理机双系统手动分区 文章目录 ubuntu22.04物理机双系统手动分区1. EFI系统分区2. 交换分区3. /根分区4. /home分区分区后的信息 手动分区顺序&#xff1a;EFI系统分区(/boot/efi)、交换分区(/swap)、/根分区、/home分区。 具体参数设置&#xff1a; 1. EFI系统分…

Java SPI 机制

SPI 机制的定义 在Java中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;机制是一种用于实现软件组件之间松耦合的方式。它允许在应用程序中定义服务接口&#xff0c;并通过在类路径中发现和加载提供该服务的实现来扩展应用程序功能。 SPI 机制通常涉及三…

霉霉说地道中文,口型、卡点几乎完美,网友:配音时代结束了?

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站 每天给大家更新可用的国内可用chatGPT资源 更多资源欢迎关注 「给电影配音的时代即将结束了。」 AI 的发展让很多人直呼饭碗被抢了&#xff0c;以前是艺术家、程序员…… 现在配音员也要失业了&a…

Docker部署一个SpringBoot项目(超级详细)

注意&#xff1a;下面的教程主要是针对 Centos7 的&#xff0c;如果使用的其他发行版会有细微的差别&#xff0c;请查看官方文档。 Docker部署一个SpringBoot项目&#xff08;超级详细&#xff09; 一、安装Docker1.卸载旧版2.配置Docker的yum库3.安装Docker4.设置开机自启动5.…

超级充电测试基础认识

超级充电技术是电动汽车&#xff08;EV&#xff09;充电技术的一种&#xff0c;它的主要目标是在最短的时间内为电动汽车充满电。这种技术的出现&#xff0c;对于推动电动汽车的普及和发展具有重要的意义。 超级充电测试是对超级充电设备和系统进行全面、深入的检查和评估&…

SQL107 将两个 SELECT 语句结合起来(二)(不用union,在where里用or)

select prod_id,quantity from OrderItems where quantity 100 or prod_id like BNBG% order by prod_id;在where子句里使用or

Windows 7 静态 IP 地址

Windows 7 静态 IP 地址 References 静态 IP 地址设置为 192.168.1.198 控制面板 -> 查看网络状态和任务 更改适配器设置 网络连接 -> 属性 TCP / IPv4 警告 - 已计划将多个默认网关用于提供单一网络 (例如 intranet 或者 Internet) 的冗余。 6.1. 关闭 redundancy VMw…

Day23 代码随想录(1刷) 二叉树

669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&#xff0c;原有的父代…

Web系统开发之——文章管理

原文地址&#xff1a;Web系统开发之——文章管理 - Pleasure的博客 下面是正文内容&#xff1a; 前言 经过一番考量&#xff0c;关于Web应用系统功能部分的开发&#xff0c;决定采取基础的文字文章管理为核心功能。 不再采取前后端分阶段完成的方式&#xff0c;而是以一个一个…

机器学习模型——KNN

KNN的基本概念&#xff1a; KNN(K-Nearest Neighbor)就是k个最近的邻居的意思&#xff0c;即每个样本都可以用它最接近的k个邻居来代表。KNN常用来处理分类问题&#xff0c;但也可以用来处理回归问题。 核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某…

TCPView下载安装使用教程(图文教程)超详细

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;更多干货&#xff0c;请关注专栏《网络安全自学教程》 TCPView是微软提供的一款「查看网络连接」和进程的工具&#xff0c;常用来查看电脑上的TCP/UDP连接…

水位计在水利工程安全监测中起到的作用

水利工程&#xff0c;作为人类调控水资源、抵御水患以及利用水能的重要工具&#xff0c;其安全性、稳定性与高效性显得尤为关键。水位是水利工程中最基础且至关重要的参数&#xff0c;其精确且实时的监测对于工程的日常运行与管理具有无可替代的重要性。水位计&#xff0c;作为…

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述

系统功能 1. 集成了视频监控典型的常用功能&#xff0c;包括录像&#xff08;本地录像、云端录像&#xff08;录像计划、下载计划-无线导出&#xff09;、远程检索回放&#xff09;、实时预览&#xff08;PTZ云台操控、轮播、多屏操控等&#xff09;、地图-轨迹回放、语音对讲…
最新文章