动态规划-状态压缩DP

news2024/11/29 4:03:30

[SCOI2005] 互不侵犯

题目描述

https://www.luogu.com.cn/problem/P1896

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

注:数据有加强(2018/4/25)

输入格式

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

输出格式

所得的方案数

样例 #1

样例输入 #1

3 2

样例输出 #1

16

思路

学习状态压缩DP,首先需要熟悉各种位运算:

  • &:两位都为1才得1
  • |:有1得1
  • ^:相同为0,不同为1
  • ~:0变1,1变0
  • <<:左移一次相当于✖️2,例如1<<n相当于 2 n 2^n 2n, 5<<3相当于 5 ∗ 2 3 5*2^3 523
  • >>:右移:相当于➗2

为了求解本题,我们使用二进制数记录每行都状态:例如101代表第一格和第三格放国王,接着我们先筛选出行内合法的方案:

  1. 行内合法: 如果!(i&i>>1)为真,则i合法:

    i=5  1 0 1  i=6  1 1 0
    		 0 1 0  		 0 1 1
    
  2. 行间兼容:如果!(a&b)&&!(a&b>>1) &&!(a&b<<1)为真,则a,b兼容

    a&b表示列方向上不可以同时为1

    a&b>>1表示左对角不可以放1

    a&b<<1表示右对角不可以放1

  3. 状态表示:f[i,j,a]表示前i行已经放了j个国王,第i行的第a个状态的方案数

image.png

img

状态表示:f[i,j,a]表示前i行已经放了j个国王,第i行的第a个状态的方案数

  • 统计每个合法状态包含的国王数:看有几个1
  • img
  • 优先级:<<优先级高于&

代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

int n, k;//棋盘行数,国王总数
int cnt = 0;//同一行的合法状态个数
int s[1 << 12];//同一行的合法状态集
int num[1 << 12];//每个合法状态包含的国王数
int f[12][144][1 << 12];//f[i,j,a]表示前i行放了j个国王,第i行第a个状态时的方案数

signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin >> n >> k;

    //预处理
    for (int i = 0; i < (1 << n); i++)//枚举一行的所有状态
        if (!(i & i >> 1))//如果不存在相邻的1
        {
            s[cnt++] = i;//保存一行的合法状态
            for (int j = 0; j < n; j++)
                num[i] += (i >> j & 1);//统计每个合法状态包含的国王数(看里面有几个1)
        }

    f[0][0][0] = 1;//不放国王也是一种方案

    for (int i = 1; i <= n + 1; i++)//枚举行
        for (int j = 0; j <= k; j++)//枚举国王数
            for (int a = 0; a < cnt; a++)//枚举第i行的合法状态
                for (int b = 0; b < cnt; b++)//枚举第i-1行的合法状态
                {
                    int c = num[s[a]];//第i行第a个状态的国王数
                    //可以继续放国王,不存在同列的1,不存在斜对角的1
                    if ((j >= c) && !(s[b] & s[a]) && !(s[b] & s[a] << 1) && !(s[b] & (s[a] >> 1)))
                        f[i][j][a] += f[i - 1][j - c][b];//从第i-1行向第i行转移
                }
    cout << f[n + 1][k][0];
    return 0;
}

[USACO06NOV]Corn Fields G 玉米田

题目描述

https://www.luogu.com.cn/problem/P1879

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can’t be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

农场主 J o h n \rm John John 新买了一块长方形的新牧场,这块牧场被划分成 M M M N N N ( 1 ≤ M ≤ 12 ; 1 ≤ N ≤ 12 ) (1 \le M \le 12; 1 \le N \le 12) (1M12;1N12),每一格都是一块正方形的土地。 J o h n \rm John John 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 J o h n \rm John John 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

J o h n \rm John John 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

输入格式

第一行:两个整数 M M M N N N,用空格隔开。

2 2 2 到第 M + 1 M+1 M+1 行:每行包含 N N N 个用空格隔开的整数,描述了每块土地的状态。第 i + 1 i+1 i+1 行描述了第 i i i 行的土地,所有整数均为 0 0 0 1 1 1 ,是 1 1 1 的话,表示这块土地足够肥沃, 0 0 0 则表示这块土地不适合种草。

输出格式

一个整数,即牧场分配总方案数除以 100 , 000 , 000 100,000,000 100,000,000 的余数。

样例 #1

样例输入 #1

2 3
1 1 1
0 1 0

样例输出 #1

9

思路

  1. 行内合法: 如果!(i&i>>1)为真,则i合法:

    i=5  1 0 1  i=6  1 1 0
    		 0 1 0  		 0 1 1
    
  2. 行间兼容:如果!(a&b)&&(a&g[i]==a)为真,则a,b兼容

    如果g[i]的某一位为0的话,a的那一位只可以为0,

    如果g[i]的某一位为1的化,a的那一位可以为1或0

    即 a&g[i]==a

状态表示:f[i,a]表示所有已经摆完前i行,并且第i行的状态是a的所有摆放方案的集合的数量

状态计算: f [ i , a ] = ∑ f [ i − 1 , b ] f[i,a]=\sum f[i-1,b] f[i,a]=f[i1,b]

代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

const int mod = 1e9;
int n, m;//玉米田的行数、列数
int g[14];//各行的状态值
int cnt;//同一行的合法状态个数
int s[1 << 14];//一行的合法状态集
int f[14][1 << 14];//f[i,a]表示已经种植前i行,第i行第a各状态时的方案数

signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin >> n >> m;
    //预处理
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            int x;
            cin >> x;
            g[i] = (g[i] << 1) + x;//保存各行的状态值
        }
    }

    for (int i = 0; i < 1 << m; i++)//枚举一行所有状态
        if (!(i & i >> 1))//如果不存在相邻的1
            s[cnt++] = i;

    f[0][0] = 1;//什么都不种植也是一种方案;
    for (int i = 1; i <= n + 1; i++)//枚举行
        for (int a = 0; a < cnt; a++)//枚举第i行的合法状态
            for (int b = 0; b < cnt; b++) //枚举第i-1行合法状态
            {
                //a种植在肥沃的土地上;a,b同列不同时为1
                if ((s[a] & g[i]) == s[a] && !(s[a] & s[b]))
                    f[i][a] = (f[i][a] + f[i - 1][b]) % mod;
            }
    cout << f[n + 1][0];

    return 0;
}

[NOI2001] 炮兵阵地

题目描述

https://www.luogu.com.cn/problem/P2704

司令部的将军们打算在 N × M N\times M N×M 的网格地图上部署他们的炮兵部队。

一个 N × M N\times M N×M 的地图由 N N N M M M 列组成,地图的每一格可能是山地(用 H \texttt{H} H 表示),也可能是平原(用 P \texttt{P} P 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示 N N N M M M

接下来的 N N N 行,每一行含有连续的 M M M 个字符,按顺序表示地图中每一行的数据。

输出格式

一行一个整数,表示最多能摆放的炮兵部队的数量。

样例 #1

样例输入 #1

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

样例输出 #1

6

提示

对于 100 % 100\% 100% 的数据, N ≤ 100 N\le 100 N100 M ≤ 10 M\le 10 M10,保证字符仅包含 PH

思路

  1. 行内合法:!(i&i>>1) && !(i&i>>2)为真,则i合法
  2. 行间合法:!(a&b) &&!(a&c) &&!(b&c) &&(a&g[i]==a)为真,则a,b,c合法

状态表示:f[i,a,b]表示已经摆放了前i行,当前行是第i行的第a个状态,第i-1行的第b个状态能拜访的最大数量

状态计算:f[i,a,b]=max(f[i,a,b],f[i-1,b,c]+num[a])

空间压缩优化:只需要把i改为i&1

代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

const int N = 110, M = 1 << 10;
int n, m;//行数,列数
int g[N];//保存地图各行数值
int cnt;//同一行的合法状态个数
int s[M];//同一行的合法状态集
int num[M];//每个合法状态包含1的个数
int f[N][M][M];//f[i,a,b]表示已经放好前i行,第i行第a各状态,第i-1行第b各状态时,能放置的最大数量



signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            char c;
            cin >> c;
            if (c == 'P') g[i] = (g[i] << 1) + 1;
            else g[i] = g[i] << 1;
        }
    }
    for (int i = 0; i < 1 << m; i++) {
        if (!(i & i >> 1) && !(i & i >> 2)) {
            s[cnt++] = i;
            for (int j = 0; j < m; j++) num[i] += (i >> j & 1);
        }
    }
    for (int i = 1; i <= n + 2; i++) {
        for (int a = 0; a < cnt; a++) {
            for (int b = 0; b < cnt; b++) {
                for (int c = 0; c < cnt; c++) {
                    if (!(s[a] & s[b]) && !(s[a] & s[c]) && !(s[b] & s[c]) &&
                        (g[i] & s[a]) == s[a] && (g[i - 1] & s[b]) == s[b])
                        f[i][a][b] = max(f[i][a][b], f[i - 1][b][c] + num[s[a]]);
                }
            }
        }
    }
    cout << f[n + 2][0][0];


    return 0;
}

蒙德里安的梦想

题目

链接:https://www.acwing.com/problem/content/293/

求把 N×M 的棋盘分割成若干个 1×2的长方形,有多少种方案。

例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3种方案。

如下图所示:

2411_1.jpg

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 N和 M。

当输入用例 N=0,M=0时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

数据范围

1≤N,M≤11

输入样例:

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

输出样例:

1
0
1
2
3
5
144
51205

思路

image.png

代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

const int N = 12, M = 1 << 12;
int st[M];
int f[N][M];


signed main() {
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    int n, m;
    while (cin >> n >> m && (n || m)) {

        for (int i = 0; i < 1 << n; i++) {
            int cnt = 0;
            st[i] = true;
            for (int j = 0; j < n; j++)
                if (i >> j & 1) {
                    if (cnt & 1) {
                        st[i] = false; // cnt 为当前已经存在多少个连续的0
                        break;
                    }
                } else cnt++;
            if (cnt & 1) st[i] = false; // 扫完后要判断一下最后一段有多少个连续的0
        }

        memset(f, 0, sizeof f);
        f[0][0] = 1;
        for (int i = 1; i <= m; i++)//枚举列
            for (int j = 0; j < 1 << n; j++)//枚举第i列第状态
                for (int k = 0; k < 1 << n; k++)//枚举第i-1列的状态
                    //两列状态兼容:	不出现重叠1,不出现
                    if ((j & k) == 0 && (st[j | k]))
                        // j & k == 0 表示 i 列和 i - 1列同一行不同时捅出来
                        // st[j | k] == 1 表示 在 i 列状态 j, i - 1 列状态 k 的情况下是合法的.
                        f[i][j] += f[i - 1][k];
        cout << f[m][0] << endl;
    }


    return 0;
}

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

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

相关文章

ADS-B接收机Radarcape

1.设备简介 Radarcape是一款便携、高性能、功能强大的ADS-B地面接收机。Radarcape的设备清单包含&#xff1a;ADS-B接收机主机&#xff0c;专业级ADS-B天线&#xff0c;GPS天线&#xff0c;电源线&#xff0c;网线。 2. 功能特点 Radarcape可以通过网口输出飞机的原始数据D…

开源字节 CRM 系统

开源字节CRM是一款SaaS模式的客户关系管理软件&#xff0c;基于钉钉平台进行研发&#xff0c;以客户管理为核心&#xff0c;包含客户管理、销售全流程管理&#xff0c;合同订单、工单管理、移动审批、数据分析六大模块。 旨在助力企业销售全流程精细化、数字化管理&#xff0c…

Godot引擎 4.0 文档 - 入门介绍 - Godot简介

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a;Introduction to Godsot — Godot Engine (stable) documentation in English Godot简介 本文旨在帮助您确定 Godot 是否适合您。我们将介绍该引擎的一些广泛功能&#…

Linux中文件描述符fd和文件指针filp的理解

简单归纳&#xff1a;fd只是一个整数&#xff0c;在open时产生。起到一个索引的作用&#xff0c;进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。 文件描述符的操作(如: open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件…

Linux Audio (4) DAPM-1 Kcontrol

DAPM-1 Kcontrol 控制部件之kcontrolsnd_kcontrol_new 结构体如何定义snd_kcontrol_new?如何使用snd_kcontrol&#xff1f;添加kcontrol代码分析 课程&#xff1a;韦东山音频专题 内核&#xff1a;Kernel 3.5 但是我用的实例和课程不同&#xff0c;以防止编程记流水账 控制部件…

【周末闲谈】你知道物联网技术吗?

连接万物&#xff0c;创造未来。从智能家居到智慧医疗&#xff0c;从智能车联到智慧城市&#xff0c;物联网技术的影响已经悄然渗透到了我们的方方面面。欢迎大家积极讨论联网技术如何影响了我们的生活。 个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【…

微软和OpenAI联手推出了GitHub Copilot这一AI编程工具,可根据开发者的输入和上下文,生成高质量的代码片段和建议

只需要写写注释&#xff0c;就能生成能够运行的代码&#xff1f;对于程序员群体来说&#xff0c;这绝对是一个提高生产力的超级工具&#xff0c;令人难以置信。实际上&#xff0c;早在2021年6月&#xff0c;微软和OpenAI联手推出了GitHub Copilot这一AI编程工具。它能够根据开发…

【计算机网络复习】第四章 网络层 3

路由器的功能和层次 o 计算机网络的核心设备 o 具有多个输入接口和多个输出接口 o 任务是转发IP包&#xff1a;将从某个输入接口收到的I包&#xff0c;按照要去的目的地&#xff08;即目的网络&#xff09;&#xff0c;从路由器的某个合适的输出接口转发给下一跳路由器 …

基于ssm+vue的驾校在线培训平台

基于ssmvue的驾校在线培训平台 系统功能 普通用户 新闻咨讯&#xff1a;可以查看系统新闻并进行评论、收藏和点赞 教资信息查看&#xff1a;普通用户登录系统可以查看驾校教资情况 系统通知信息&#xff1a;用户可以查看网站相关通知公告信息 在线报名&#xff1a;普通用户可…

C++ 初始模板

模板 void Swap(int* x, int* y) {int tmp *x;*x *y;*y tmp; }void Swap(double* x, double* y) {double tmp *x;*x *y;*y tmp; }void Swap(char* x, char* y) {char tmp *x;*x *y;*y tmp; } 如上述所示&#xff0c;我们在实现函数的时候&#xff0c;有很多函数会像…

【C++ 入坑指南】(09)数组

文章目录 简介一维数组1. 定义2. 特点3. 用途4. 示例 二维数组1. 定义2. 用途3. 示例 简介 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 一维数组 1. 定义…

外企还是香啊~

小伙伴们大家好&#xff0c;我是阿秀。 三月份的时候我看了下外企&#xff0c;查了一些资料&#xff0c;最后查下来远远比我想的要多&#xff0c;可能很多人跟我一样&#xff0c;对外企的印象都停留在微软、谷歌、intel这些比较市值大的公司上。 其实远远不止&#xff0c;广义上…

密码学安全性证明(一)Cramer-Shoup密码系统

Cramer-Shoup密码系统来自于A Practical Public Key CryptosystemProvably Secure against Adaptive ChosenCiphertext Attack这篇论文 CDH问题回顾&#xff1a; 已知(g,g^x, gk)能否计算gxk DDH问题回顾&#xff1a; 已知(g,g^x, g^k &#xff0c;D)能否判断D是否等于g^xk 注意…

港科夜闻|香港科技大学赛马会研究院两位成员入选美国国家科学院

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科技大学赛马会研究院两位成员入选美国国家科学院。香港科技大学赛马会研究院Gunther Uhlmann教授和香港科技大学客座教授戴碧瓘教授因在原创性研究方面的杰出和可持续的成就&#xff0c;入选美国国家科学院(NAS)。G…

API接口的工作原理以及可以帮我们实现什么功能?

一、API接口的工作原理 API接口是应用程序编程接口(Application Programming Interface)的缩写&#xff0c;是不同软件系统之间进行通信的一种方式。 API接口的工作原理是&#xff0c;通过预定义的接口规范&#xff0c;软件系统可以调用或提供API接口的服务&#xff0c;来实现…

【开源项目】权限框架Nepxion Permission原理解析

项目介绍 Nepxion Permission是一款基于Spring Cloud的微服务API权限框架&#xff0c;并通过Redis分布式缓存进行权限缓存。它采用Nepxion Matrix AOP框架进行切面实现&#xff0c;支持注解调用方式&#xff0c;也支持Rest调用方式 项目地址 https://toscode.gitee.com/nepxion…

实现图形算法API[软光栅渲染器,C++]

最近有点烦&#xff0c;发烧感冒了三天[事实上是俩天&#xff0c;第三天是因为摆得太舒服了索性多玩一天]&#xff0c;啥都没学&#xff0c;打守望先锋也把把被虐...&#xff0c;想着今天来提起键盘把之前的东西都总结一下。 那么话归真题&#xff0c;首先我是仿造opengl来写的…

tensorflow/keras如何自定义layer

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【Linux高级 I/O(3)】如何使用阻塞 I/O 与非阻塞 I/O?——poll()函数

poll()函数介绍 系统调用 poll()与 select()函数很相似&#xff0c;但函数接口有所不同。在 select()函数中&#xff0c;我们提供三个 fd_set 集合&#xff0c;在每个集合中添加我们关心的文件描述符&#xff1b;而在 poll()函数中&#xff0c;则需要构造一个 struct pollfd 类…

opencv图像增强实现方法

opencv是一款开源的图像增强工具&#xff0c;主要用于在 python环境下实现图像增强功能。 使用 opencv实现图像增强&#xff0c;需要使用 opencv的 GUI模块&#xff0c;如图1所示。 在 opencv中&#xff0c;有一个 datasets模块&#xff0c;这个模块主要用于处理数据和可视化操…