3254 Corn Fields 这题解真的不能再详细了!

2023-11-19

题意

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

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

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

输入格式

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

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

输出格式

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

详细思路及代码逻辑

建议先做一下炮兵阵地,双倍经验。拿到题先估计,这是一个状态压缩的动归,为什么呢?因为数据很动归,而且矩阵的元素非0即1,很适合状态压缩。那么怎么进行下去呢,我们主要由以下几个问题。

  1. 对谁进行状态压缩?
  2. 动态规划的思路,转移方程是什么?
  3. 最终结果如何得到?

首先我们考虑对哪个元素进行压缩,对全局的矩阵嘛?不合适,太大了,就经验来看,矩阵形式的DP一般都压缩每一行的状态,然后一行一行遍历,我们不妨试一试使用dp[i][s]表示,到第i行,状态为s时可能的种植方案数目,那么我们可以得到这样一个递推关系式 d p [ i ] [ s ] = ∑ k = 0 m d p [ i − 1 ] [ k ] dp[i][s]=\sum_{k=0}^{m}dp[i-1][k] dp[i][s]=k=0mdp[i1][k]
其中 m m m是所有与 s s s不冲突而且可以在 i − 1 i-1 i1行使用的状态。那么我们至少需要如此一个for循环

for (int i = 1; i < M; i++) {//遍历第2-M行
	//转移条件
	for (int k = 0; k < cnt; k++) {//遍历第i行的所有状态
		//转移条件
		for (int m = 0; m < cnt; m++) {
			//转移条件
			dp[i][k] = (dp[i][k] % mod + dp[i - 1][m] % mod) % mod;
		}
	}
}

cnt是可用的状态总数目,10个位置的话总共也就是1024,不过我们会对他进行一步优化,使其远远比1024小(之后再说)。我们先填充这些转移条件,有什么条件呢?

  1. 不能种在不肥沃的土地上!比如第一行是101,在输入时我们将不肥沃的土地转化为1,其余视为0,那么表示为010,此时我们的状态是110(1表示种了,0没种),可以看到只要我们将二者按位与,如果结果是1,那么肯定在不肥沃的土地上种植了,该状态不可以用。tips:使用一个数组mmap保存所有行的土地状况。
for (int i = 0; i < M; i++) {
	for (int j = 0; j < N; j++) {
		int a; cin >> a;
		if (a == 0) mmap[i] |= (1 << j);
	}
}
  1. 上一行和这一行不能有相邻节点。同样我们使用按位与操作,如果两行在某个位置上都种植了,比如11001000,那么按位与的结果一定是1.
  2. 同一行也不能有相邻节点。对这个要求,我们可以对每一行能选择的状态进行预处理,左右有相邻元素的状态可以抛掉,只保留可以用的状态(不仅减小内存,而且加快速度)
s[0] = 0;
for (int i = 1; i < (1 << N); i++) {
	if (i&(i << 1) || i & (i >> 1))continue;//左右有相连的种植土地
	s[cnt++] = i;
}

好我们现在补全for循环

for (int i = 1; i < M; i++) {//遍历第2-M行
	for (int k = 0; k < cnt; k++) {//遍历第i行的所有状态
		//k状态和i的地形冲突
		if (mmap[i] & s[k]) continue;
		for (int m = 0; m < cnt; m++) {
			//m状态与i-1行的地形冲突或者m状态与k状态有临边
			if ((mmap[i - 1] & s[m]) || (s[m] & s[k])) continue;
			dp[i][k] = (dp[i][k] % mod + dp[i - 1][m] % mod) % mod;
		}
	}
}

然后还有一件事别忘了,就是第一行需要特判处理,每个可行的方案基础值都是1,等等?不是方案总数嘛?为什么用1来替代—因为我们会遍历所有的情况,再回顾递推公式 d p [ i ] [ s ] = ∑ k = 0 m d p [ i − 1 ] [ k ] dp[i][s]=\sum_{k=0}^{m}dp[i-1][k] dp[i][s]=k=0mdp[i1][k],只要是可能的组合我们都会直接加上,对1001这种形式,10000001的组合我们都已经遍历过了,因此只需要将num[9(1001)]赋值为1即可。

for (int i = 0; i < cnt; i++) {
	if (mmap[0] & s[i])continue;
	dp[0][i] = 1;
}

最后,把第N行的所有可能结果加起来就是我们需要的结果了。

int res = 0;
for (int i = 0; i < cnt; i++) res = (res%mod + dp[M - 1][i] % mod) % mod;
cout << res << endl;
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

#define MAX 15
#define mod 100000000
int M, N, cnt = 1, mmap[MAX];
//dp[i][s]:遍历到第i行,状态为s时的种植方案总数,num[s]:状态s有几种种植方案, s:可用状态集合
int dp[MAX][1024], num[1024], s[1024];

int count1(int k) {
	int ans = 0;
	while (k > 0) {
		ans++;
		k -= (k&(-k));
	}
	return ans;
}

int main() {
	cin >> M >> N;
	for (int i = 0; i < M; i++) {
		for (int j = 0; j < N; j++) {
			int a; cin >> a;
			if (a == 0) mmap[i] |= (1 << j);
		}
	}

	s[0] = 0;
	for (int i = 1; i < (1 << N); i++) {
		if (i&(i << 1) || i & (i >> 1))continue;//左右有相连的种植土地
		s[cnt++] = i;
	}

	for (int i = 0; i < cnt; i++) {
		if (mmap[0] & s[i])continue;
		dp[0][i] = 1;
	}

	for (int i = 1; i < M; i++) {//遍历第2-M行
		for (int k = 0; k < cnt; k++) {//遍历第i行的所有状态
			//k状态和i的地形冲突
			if (mmap[i] & s[k]) continue;
			for (int m = 0; m < cnt; m++) {
				//m状态与i-1行的地形冲突或者m状态与k状态有临边
				if ((mmap[i - 1] & s[m]) || (s[m] & s[k])) continue;
				dp[i][k] = (dp[i][k] % mod + dp[i - 1][m] % mod) % mod;
			}
		}
	}

	int res = 0;
	for (int i = 0; i < cnt; i++) res = (res%mod + dp[M - 1][i] % mod) % mod;
	cout << res << endl;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

3254 Corn Fields 这题解真的不能再详细了! 的相关文章

  • 程序员简历应该怎么写?

    说到程序员简历 这两个月 我看过不下10 000份简历 答主不是HR 也不是技术负责人 但是在网站的运营工作中 每天最开心的事情就是研究候选人的简历了 这些人中 有BAT的资深大牛程序员 也有90后程序员小鲜肉 有人到中年的程序员渴望去创业
  • MyBatis参数传入集合之foreach动态sql

    foreach的主要用在构建in条件中 它可以在SQL语句中进行迭代一个集合 foreach元素的属性主要有item index collection open separator close item表示集合中每一个元素进行迭代时的别名

随机推荐

  • 期货反向跟单--交易员的培养问题

    根据我们统计的数据显示 今年做国内期货反向跟单的团队 无论是从赢利金额 稳定性 还是成功概率 都比做国际期货的团队要高 尤其是最近纯碱 焦煤焦炭 PTA 红枣等几个品种的行情 更是频繁拉爆了很多盘手的账户 本文转发自公众号 反跟单交易 转载
  • 【Mysql】Communications link failure,The last packet sent successfully to the server was 0 millisecond

    项目背景是数据库和项目不在同一台服务器下 在启动时 突然遇到以下错误 Exception in thread main com mysql jdbc exceptions jdbc4 CommunicationsException Comm
  • Java图书馆

    io流用的不是很熟练 还有Book类的应用出了点问题 越改越错 从2个错误改到102个QAQ 孩子想哭 问了好多人也没改成 最后勉强成型 而且上个星期内分泌系统出了点小问题 天天往医院跑 开始敲的太晚了 现在要备战期末考 等期末考结束再改改
  • Linux系统编程:多线程交替打印ABC

    引言 分享关于线程的一道测试题 因为网上基本都是Java的解决方法 决定自己写一篇来记录一下线程的学习 问题描述 编写一个至少具有三个线程的程序 称之为线程 A B 和 C 其中线程 A 输出字符 A 线程 B 输出字符 B 线程 C 输出
  • Spring源码深度解析:文章目录

    文章目录 序号 内容 链接地址 1 一 Spring整体架构和源码环境搭建 https blog csdn net wts563540 article details 126686645 2 二 手写模拟Spring https blog
  • Windows server 2016 部署 AD域

    AD域的简单介绍 为什么要使用域 假设你是协会的系统管理员 管理高职部所有的机房 如果你要为每台电脑设置登录帐户 设置权限 比如是否允许登录帐户安装软件 那你要分别坐在所有电脑前一一设置 如果你要做一些改变 你也要分别在这所有电脑上修改 相
  • 【论文解读】NLP重铸篇之Word2vec

    论文标题 Efficient Estimation of Word Representations in Vector Space论文链接 https arxiv org pdf 1301 3781 pdf复现代码地址 https gith
  • mysql出现“ You can't specify target table '表名' for update in FROM clause”解决方法

    You can t specify target table 表名 for update in FROM clause 翻译为 不能先select出同一表中的某些值 再update这个表 在同一语句中 实例 表 result 表studen
  • (java 基础知识) Java打印---javax.print

    package com print import java io import javax print import javax print attribute import javax print attribute standard p
  • 华为OD机试 - 快递运输(Java)

    题目描述 一辆运送快递的货车 运送的快递放在大小不等的长方体快递盒中 为了能够装载更多的快递 同时不能让货车超载 需要计算最多能装多少个快递 注 快递的体积不受限制 快递数最多1000个 货车载重最大50000 输入描述 第一行输入每个快递
  • React Native_综合练习(react-navigation)

    据说 react natvigation是官方推荐使用的 搞不懂为啥官方放弃更新natigator了 所以在上篇文章的基础上使用react natvigation 1 StackNavigator 用来跳转页面和传递参数 2 TabNavi
  • Linux系统移植:Kernel 顶层 Makefile(下)

    Linux系统移植 Kernel 顶层 Makefile 下 继续分析 Linux 内核源码顶层 Makefile 执行过程 一 make defconfig 过程 与 uboot 的顶层 makefile 相同 在编译源码前 要用 mak
  • 解决openai网站拒绝访问的问题,Access denied,You do not have access to chat.openai.com

    解决步骤 清除浏览器的历史纪录数据 尝试更换科学上网节点 开启无痕浏览模式 我通过这三个步骤登录成功了 希望可以帮助到大家
  • F5杯—网络是有记忆的

    0x00 前言 CTF 加解密合集 CTF 加解密合集 0x01 题目 网络有记忆 我也有 所以 我想她了 提示 1 题目既提示 2 flag包括小写字母 单词 下划线 IDEgOChWMyVNM1wtGVhbI1NeMCE0Vy9RHVB
  • scanf函数的读取

    scanf的处理机制 scanf 以删除的方式从缓冲区读取数据 输入设备的数据存储缓冲区 比如键盘 也就是说 scanf从缓冲区读入一个数据项 该数据项在缓冲区中就被清除掉了 而如果scanf需要读取一个数据项 返现缓冲区当前是空的 那么程
  • 《斗破CPP》 第叁章(中) ---- 左值右值问题

    斗破CPP 第叁章将会分成上中下三部分分享给大家 上 偏向于讲述循环 中 讲1个中级难度的运算符 下 偏向于讲解具有强大功能的语句以及控制符 不管有基础还是没基础的小伙伴 都可以重点看看 上 后半部分 中 前半部分 下 后半部分小总结 目录
  • java实现简单的生成52张牌、三个人洗牌、码牌算法

    定义一个Pocker类 用于定义牌类 package demo public class Poker private String suit 花色 private int rank 数字 构造函数 public Poker String s
  • Java学习day17

    异常处理 异常处理机制 代码 public class Demo01 public static void main String args int a 1 int b 0 假如要捕获多个异常 从小到大 try if b 0 throw t
  • Mybatis一对多查询,分页显示问题解决方案

    分页查询在我们的开发中也许是遇到最多的功能 一张表的分页 多张表一对一功能的分页相信大家写来都是得心应手 但是在一对多分页查询的时候大家写法不对的时候 可能会遇到查询的总条数和实际总条数对不上的问题 不多说下面请看演示 1 先提供2张表的建
  • 3254 Corn Fields 这题解真的不能再详细了!

    题意 农场主John新买了一块长方形的新牧场 这块牧场被划分成 M M M行 N N N列 1