当前位置: 首页 > news >正文

信息学奥赛一本通 1593:【例 2】牧场的安排 | 洛谷 P1879 [USACO06NOV] Corn Fields G

【题目链接】

ybt 1593:【例 2】牧场的安排
洛谷 P1879 [USACO06NOV] Corn Fields G

【题目考点】

1. 状压动规

【解题思路】

集合状态:n个元素中,选择x个元素构成的集合,可以由一个n位二进制数表示。第i位为1表示选择第i个元素,第i位为0表示不选择第i个元素。
将在某格子种草称为为该格子着色。
本题每行着色的情况可以设为一个集合状态,称为着色状态。第i位为1表示选择了这一行的第i个格子已着色,第i位为0表示第i个格子没有着色。

sis_isi表示第i行的土地状态,sis_isi第j位为1表示第i行第j个格子可以着色,为0表示不可以着色。

二进制下的数字位,从0开始,从低位到高位分别是第0位,第1位。。。
例:二进制数:110 :第0位为0, 第1位为1,第2位为1

解法1:状压动规 枚举所有状态

状态定义
  • 阶段:前i行,第i行的着色为集合状态j
    第i+1行的集合状态受到第i行着色状态的影响,因此阶段之一需要是第i行的着色状态。
  • 决策:决定一行的着色
  • 策略:网格图的着色方案
  • 策略集合:对前i行着色,且第i行着色为集合状态j的所有着色方案
  • 统计量:方案数

状态定义dpi,jdp_{i,j}dpi,j:对前i行着色,且第i行着色为集合状态j的着色方案总数。

状态转移方程
  • 策略集合:对前i行着色,且第i行着色为集合状态j的所有着色方案
  • 分割策略集合:根据第i-1行的不同着色状态来分割策略集合

在求dpi,jdp_{i,j}dpi,j时,要枚举第i行的所有着色状态j,从中筛选出符要求的着色状态。在此过程中需要枚举第i-1行所有可能的着色状态,设枚举出的第i-1行的着色状态为k。

  1. 着色的格子必须是这一行可以着色的格子的子集

    因此第i行的着色状态j表示的已着色的格子必须是sis_isi表示的已着色的格子的子集,即必须满足s[i] & j == j
    第i-1行的着色状态k表示的已着色的格子必须是si−1s_{i-1}si1表示的已着色的格子的子集,即必须满足s[i-1] & k == k

  2. 每一行不可以有相邻的两个格子都着色。
    将k左移一位k<<1k<<1中第i位数为k中第i-1位数。k<<1的最低位为0。
    那么k<<1 & k的结果,就是将k的每一位与其右侧相邻一位进行按位与运算,(由于k<<1最低位为0,那么按位与的结果最低位为0)
    如果存在k的相邻两位都是1,那么结果不为0。如果不存在k的相邻两位都是1,那么结果为0。
    因此第i-1行只有满足(k<<1 & k) == 0(或写为!(k<<1 & k))的着色状态符合要求。同理第i行只有满足!(j << 1 & j)的着色状态符合要求。

  3. 第i行的着色状态j与第i-1行的着色状态k同一列的格子不能都着色。
    也就是这两个二进制数同一数位上不能都为1。
    如果k与j某一位都为1,那么k & j不为0,否则为0。
    因此k与j需要满足(k & j) == 0(或写为!(k & j)),才不存在第i行与第i-1行纵向上有相邻的格子都着色。

对于第i行的符合要求的着色状态j,找到一个符合要求的第i-1行的着色状态k,对前i-1行着色且第i-1行的着色状态为k的着色方案数为dpi−1,kdp_{i-1,k}dpi1,k
对i-1行所有满足条件的着色状态k,将dpi−1,kdp_{i-1,k}dpi1,k加和,即为对前i行着色且第i行着色状态为j的着色方案数。
因此dpi,j=∑dpi−1,kdp_{i,j} = \sum dp_{i-1,k}dpi,j=dpi1,k,j需要满足j & s[i] == j,k需要满足k & s[i-1] == k && !(k<<1 & k) && !(k & j)

对于初始状态,本题可以枚举第一行满足没有相邻格子染色(即满足j & s[1] == j && !(j<<1 & j))的染色状态j,第一行染色状态为j的方案有一种,设dp1,j=1dp_{1,j} = 1dp1,j=1

或者假设第0行参与染色,第0行只存在染色状态为0的一种情况,因此设dp0,0=1dp_{0,0}=1dp0,0=1

最后对于第m行的所有符合j & s[m] == j的状态j,求出为前m行染色,第m行着色状态为j的方案数,并加和。即结果为∑dpm,j\sum dp_{m,j}dpm,j,j满足j & s[m] == j

注意,每次加和后都要对10810^8108取模。

解法2:状压动规 保存合法的状态

本题每一行只会取没有相邻格子着色的状态,那么可以先将这些一定没有相邻格子同时着色的集合状态预处理出来,保存在state数组,state[i]表示第i个可用的集合状态。
而后dpi,jdp_{i,j}dpi,j表示的是对前i行着色,且第i行着色状态为state[j]的方案数。
接下来在判断着色状态是否符合要求时,就不用判断“要求相邻格子不能同时着色”这一点了,即不用再写j<<1 & jk<<1 & k

【题解代码】

解法1:状压动规 枚举所有状态
  • 写法1:设第1行的状态作为初始状态
#include<bits/stdc++.h>
using namespace std;
const int N = 12+5, S = (1<<12)+5, M = 1e8;
int m, n, s[N];//s[i]:第i行可以种草的集合状态 1表示可以种,0表示不可以种 
long long dp[N][S], ans;//dp[i][j]:前i行着色,第i行集合状态为j的方案数 
int main()
{int x;cin >> m >> n;for(int i = 1; i <= m; ++i)for(int j = 1; j <= n; ++j){cin >> x;s[i] = s[i]<<1 | x;}for(int j = 0; j < 1<<n; ++j) if((j & s[1]) == j && !(j & j<<1))//j不是s[i]的子集 或 j有相邻的着色格 dp[1][j] = 1;for(int i = 2; i <= m; ++i)for(int j = 0; j < 1<<n; ++j) if((j & s[i]) == j && !(j & j<<1))//j是s[i]的子集 同时 j没有相邻的着色格 for(int k = 0; k < 1<<n; ++k) if((k & s[i-1]) == k && !(k & k<<1) && !(k & j))//k不是s[i-1]的子集,或k有相邻着色格,或k与j有上下相邻的着色格 dp[i][j] = (dp[i][j]+dp[i-1][k])%M; for(int j = 0; j < 1<<n; ++j) if((j & s[m]) == j && !(j & j<<1))ans = (ans+dp[m][j])%M;cout << ans;return 0;
}
  • 写法2:设第0行的状态作为初始状态
#include<bits/stdc++.h>
using namespace std;
const int N = 12+5, S = (1<<12)+5, M = 1e8;
int m, n, s[N];//s[i]:第i行可以种草的集合状态 1表示可以种,0表示不可以种 
long long dp[N][S], ans;//dp[i][j]:前i行着色,第i行集合状态为j的方案数 
int main()
{int x;cin >> m >> n;for(int i = 1; i <= m; ++i)for(int j = 1; j <= n; ++j){cin >> x;s[i] = s[i]<<1 | x;}dp[0][0] = 1;for(int i = 1; i <= m; ++i)for(int j = 0; j < 1<<n; ++j) if((j & s[i]) == j && !(j & j<<1))//j是s[i]的子集 同时 j没有相邻的着色格 for(int k = 0; k < 1<<n; ++k) if((k & s[i-1]) == k && !(k & k<<1) && !(k & j))//k不是s[i-1]的子集,或k有相邻着色格,或k与j有上下相邻的着色格 dp[i][j] = (dp[i][j]+dp[i-1][k])%M; for(int j = 0; j < 1<<n; ++j) if((j & s[m]) == j && !(j & j<<1))ans = (ans+dp[m][j])%M;cout << ans;return 0;
}

解法2:状压动规 保存合法的状态

#include<bits/stdc++.h>
using namespace std;
const int N = 12+5, S = (1<<12)+5, M = 1e8;
int m, n, s[N];//s[i]:第i行可以种草的集合状态 1表示可以种,0表示不可以种 
int state[S], sn;//state[i]:第i个符合条件的集合状态 
long long dp[N][S], ans;//dp[i][j]:前i行着色,第i行集合状态为state[j]的方案数 
int main()
{int x;cin >> m >> n;for(int i = 1; i <= m; ++i)for(int j = 1; j <= n; ++j){cin >> x;s[i] = s[i]<<1 | x;}for(int i = 0; i < 1<<n; ++i) if((i & i<<1) == 0)state[++sn] = i;//把没有相邻的着色格的集合状态加入state for(int j = 1; j <= sn; ++j) if((state[j] & s[1]) == state[j])dp[1][j] = 1;for(int i = 2; i <= m; ++i)for(int j = 1; j <= sn; ++j) if((state[j] & s[i]) == state[j])//j不是s[i]的子集 for(int k = 1; k <= sn; ++k) if((state[k] & s[i-1]) == state[k] && !(state[k] & state[j]))//k不是s[i-1]的子集,或k有相邻着色格,或k与j有上下相邻的着色格 dp[i][j] = (dp[i][j]+dp[i-1][k])%M;for(int j = 1; j <= sn; ++j) if((state[j] & s[m]) == state[j])ans = (ans+dp[m][j])%M;cout << ans;return 0;
}
http://www.dtcms.com/a/297223.html

相关文章:

  • 机器学习——KNN算法
  • SpringBoot与ApacheSpark、MyBatis实战整合
  • DeepSeek FlashMLA 技术拆解,AI 推理迎来颠覆性突破
  • 黑马点评常见面试题
  • Apache Ranger 权限管理
  • Python之--字典
  • CMake进阶: 检查函数/符号存在性、检查类型/关键字/表达式有效性和检查编译器特性
  • LP-MSPM0G3507学习--11ADC之二双通道高速DMA采样
  • rtpengine的docker化
  • Linux进程信号——信号保存
  • 在幸狐RV1106板子上用gcc14.2本地编译安装ssh客户端/服务器、vim编辑器、sl和vsftpd服务器
  • OSI 七层模型和五层模型
  • Vue3 学习教程,从入门到精通,Vue3 监听属性(Watchers)语法知识点及案例代码(16)
  • Unity编辑器拓展 IMGUI与部分Utility知识总结(代码+思维导图)
  • JAVA-09(2025.07.25学习记录)
  • MMRotate ReDet ReFPN 报错 `assert input.type == self.in_type`
  • Franky — 边缘计算智能语音助手 / Edge‑Computing Smart Voice Assistant
  • 04-netty基础-Reactor三种模型
  • docker compose xtify-music-web
  • 华为OpenStack架构学习9篇 连载—— 02 OpenStack界面管理【附全文阅读】
  • VR 三维重建:重塑建筑工程全生命周期的数字化革命
  • [NLP]多电源域设计的仿真验证方法
  • Redis 5.0.14安装教程
  • Android 10.0 sts CtsSecurityBulletinHostTestCases的相关异常分析
  • 关于自定义域和 GitHub Pages(Windows)
  • OpenCV(04)梯度处理,边缘检测,绘制轮廓,凸包特征检测,轮廓特征查找
  • [python][flask]Flask-Login 使用详解
  • uniapp小程序上传图片并压缩
  • 吊汤:厨房的鲜味密码
  • 若依框架 ---一套快速开发平台