[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 5∗23
  • >>:右移:相当于➗2

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

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

    i=5  1 0 1  i=6  1 1 00 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个状态的方案数

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

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

代码

#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_JUDGEfreopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#endifcin >> 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,不存在斜对角的1if ((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) (1≤M≤12;1≤N≤12),每一格都是一块正方形的土地。 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 00 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[i−1,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_JUDGEfreopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#endifcin >> 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))//如果不存在相邻的1s[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同列不同时为1if ((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 N≤100, M ≤ 10 M\le 10 M≤10,保证字符仅包含 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_JUDGEfreopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#endifcin >> 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种方案。

如下图所示:

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 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

思路

代码

#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_JUDGEfreopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
#endifint 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 为当前已经存在多少个连续的0break;}} 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;
}

动态规划-状态压缩DP相关推荐

  1. 0x56. 动态规划 - 状态压缩DP(习题详解 × 7)

    目录 Problem A. 最短Hamilton路径 ProblemB. 蒙德里安的梦想 Problem C. Corn Fields Problem D. 小国王 Problem E. 炮兵阵地 P ...

  2. 由NP完全问题引出动态规划——状态压缩DP

    " 所有部分都应当在非强制的情况下组合回一起.要记住,你重组的那部分原来就是你拆解的.因此,如果你不能让它们组合回来的话,那一定是有原因的.要想尽一切办法,除了用锤头." – IB ...

  3. 第一章 动态规划 状态压缩DP

    1.基本概述 状态压缩dp和状态机一样,都是一种特殊的状态表示方式.状态机用一系列小状态表示某一状态.状态压缩dp用二进制数进行表示.虽然看代码起来时间复杂度比较高,但是很多的情况都给剪枝掉了. 状态 ...

  4. 动态规划——状态压缩dp

    文章目录 概述 状态压缩 使用条件 状压dp 位运算 棋盘(基于连通性)类问题 概述 例题 蒙德里安的梦想 小国王 玉米田 炮兵阵地 集合类问题 概述 例题 最短Hamilton路径 愤怒的小鸟 总结 ...

  5. sdoi2009 [动态规划 状态压缩DP] 学校食堂

    背景 飘逸的EWF组合~ 描述 小F的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味 ...

  6. 动态规划(5)状态压缩dp

    一.概述 动态规划的过程是随着阶段不断增长,在每个状态维度上不断扩展.在任意时刻,已经求出最优解的状态与尚未求出最优解的状态在各维度上的分界点组成了dp扩展的轮廓,对于某些问题,我们需要在动态规划的状 ...

  7. 状态压缩DP AcWing算法提高课 (详解)

    基础课的状态压缩点这里 基础课中 蒙德里安的梦想 属于 棋盘式状态压缩dp,最短Hamilton路径 属于 集合状态压缩dp 1064. 小国王(棋盘式/基于连通性) 这种棋盘放置类问题,在没有事先知 ...

  8. 状态压缩DP(大佬写的很好,转来看)

    奉上大佬博客 https://blog.csdn.net/accry/article/details/6607703 动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的 ...

  9. [转]状态压缩dp(状压dp)

    状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴. 为了更好的理解状压dp,首先介绍位运算相关的 ...

最新文章

  1. 信号转换 | 将对称方波转换成倍频PWM波形
  2. 如何在windows7和windows8双系统中卸载windows8
  3. ARTS打卡计划第六周
  4. [转贴]ASP优化之显示数据查询内容
  5. Mac10.9 Mavericks 输入法切换快捷键
  6. FJ的字符串java问题_蓝桥杯VIP试题 之 基础练习 FJ的字符串- JAVA
  7. 罗格斯大学电子与计算机系排名,罗格斯大学美国大学排名及专业排名汇总(USNEWS美国大学排名版)...
  8. java for语句(翻译自Java Tutorials)
  9. STL之ForwordList
  10. 始终都要覆盖toString()方法
  11. 水系图一般在哪里找得到_水系电池再发Nature,事实力证或将迎来发展的春天!...
  12. word标题在大纲视图下统一升降级
  13. 解决go get时,遇到unrecognized import path的问题
  14. 镁光 鸡血模式 linux,所以,镁光1100到底是不是全盘slc的模式?之前就几个帖子...
  15. 一种TV端自动化测试应用OTA升级的方法
  16. 逻辑归纳与数学归纳:皮亚诺公理5解读1——皮亚诺读后之七
  17. npm 配置项registry修改为淘宝镜像
  18. Socket传输字符串数组
  19. 在jsp页面上直接打开PDF文件
  20. 【微软资源站】MSDN

热门文章

  1. 【ubuntu调节屏幕亮度】
  2. 换了个无线怎么找到服务器,路由器再接一个路由器怎么设置? | 192路由网
  3. 从img中提取图片地址
  4. php fgets 回车符号,有时在PHP ''符号影响fgets行读取时,fget不会读取完整行
  5. Python实现多项式回归实战——以及与线性回归的拟合效果对比
  6. java getitem方法_Java Datasource.getItem方法代碼示例
  7. 万物互联:区块链与大数据将擦出怎样的火花?
  8. linux alias命令路径,Linux alias命令
  9. 【iOS沉思录】iOS沙盒内存使用深入剖析
  10. nodejs如何创建桌面快捷方式