(经典dp) 骨牌问题 2*n 3*n n*m

news2024/11/19 12:38:59

文章目录

  • 前言
  • 题目
    • 2*n
    • 3*n
    • n*m
      • n < 5 && m < 1e9
      • n*m < 100
  • END

前言

C40-1003-1.jpg (589×344) (hdu.edu.cn)

1*2的骨牌铺满一个平面,是非常经典的一系列dp题目

(各大平台几乎都有这类题)

并且随着平面的要求不同,难度也是层层递增

对于n*m若数据量不同,则对应处理的算法也不一样

楼主本人还未完全理解明白这系列的所有题,因此本文主要就是为了记录代码,不做过多讲解

题目

2*n

awing:3687. 骨牌

51nod:骨牌覆盖 - 51Nod 1031

杭电:骨牌铺方格- 2046

若读者这题还不会,建议先学习学习基础的dp推导

/**
 * https://www.acwing.com/problem/content/3690/
 * 用 1×2 和 2×1 的骨牌铺满大小为 2×n 的地板,请问共有多少种不同铺法。
 */
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int mod = 999983;
const int M = 10 + 10000;

int dp[M];

signed main() {
    int n;
    cin >> n;

    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
    for (int i = 3; i <= n; i += 1) {
        // 竖着`1`,横着`二`
        dp[i] = dp[i - 1] + dp[i - 2];
        dp[i] %= mod;
    }

    cout << dp[n] << endl;

    return 0;
}

3*n

51nod:骨牌覆盖 V2 - 51Nod 1032

本题可以直接推到,也可以用状态机dp的思路


相关的思路的题目:专题:(经典dp) I型 L型 铺盖2*n


推荐题解:XTU1161 骨牌(12的骨牌铺N3的地板)

/**
 * https://vjudge.csgrandeur.cn/problem/51Nod-1032
 * 骨牌覆盖 V2
 * 3 * n 放 1*2的骨牌,求摆放种类
 */
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int mod = 1e9 + 7;
const int M = 10 + 1000;

// 定义考虑作用第i列的时候
// 第i列所处状态的方案数
int dp[M][2 * 2 * 2];

signed main() {
    int n;
    cin >> n;

    // 或者从0开始初始化
    // 0状态,铺满当作墙壁
    dp[0][0b111] = 1;
    // dp[1][0b000] = 1;
    // dp[1][0b011] = 1;
    // dp[1][0b110] = 1;
    // 110和011对称
    // 100和001对称
    // 010和101是无限循环永远为0的情况
    for (int i = 1; i <= n; i += 1) {
        dp[i][0b111] = (dp[i - 1][0b011] + dp[i - 1][0b110] + dp[i - 1][0b000]) % mod;
        // 这为什么不 2 * dp[i - 1][0b001]
        // 因为我们定义的是作用于第i列
        // 若先构成了dp[i-1][0b111]再转换则违背定义
        // 且会和单独考虑dp[i-1][0b111]冲突,或者说就是包含在这里了
        dp[i][0b110] = (dp[i - 1][0b111] + dp[i - 1][0b001]) % mod;
        dp[i][0b101] = (dp[i - 1][0b010]) % mod;
        dp[i][0b100] = (dp[i - 1][0b011]) % mod;
        dp[i][0b011] = (dp[i - 1][0b111] + dp[i - 1][0b100]) % mod;
        dp[i][0b010] = (dp[i - 1][0b101]) % mod;
        dp[i][0b001] = (dp[i - 1][0b110]) % mod;
        dp[i][0b000] = (dp[i - 1][0b111]) % mod;
    }

    cout << dp[n][0b111] << endl;
    return 0;
}

交流群的一位大佬直接用代码写代码

        dp[i][0b111] = (dp[i][0b111] + dp[i - 1][0b000]) % mod;
        dp[i][0b110] = (dp[i][0b110] + dp[i - 1][0b001]) % mod;
        dp[i][0b101] = (dp[i][0b101] + dp[i - 1][0b010]) % mod;
        dp[i][0b100] = (dp[i][0b100] + dp[i - 1][0b011]) % mod;
        dp[i][0b111] = (dp[i][0b111] + dp[i - 1][0b011]) % mod;
        dp[i][0b011] = (dp[i][0b011] + dp[i - 1][0b100]) % mod;
        dp[i][0b010] = (dp[i][0b010] + dp[i - 1][0b101]) % mod;
        dp[i][0b001] = (dp[i][0b001] + dp[i - 1][0b110]) % mod;
        dp[i][0b111] = (dp[i][0b111] + dp[i - 1][0b110]) % mod;
        dp[i][0b000] = (dp[i][0b000] + dp[i - 1][0b111]) % mod;
        dp[i][0b011] = (dp[i][0b011] + dp[i - 1][0b111]) % mod;
        dp[i][0b110] = (dp[i][0b110] + dp[i - 1][0b111]) % mod;

///    

	private static String not(String state) {
		char[] data = state.toCharArray();
		for (int i = 0; i < data.length; i++) {
			data[i] = data[i] == '1' ? '0' : '1';
		}
		return String.valueOf(data);
	}
	
	private static boolean canConvertTo(String state1, String state2) {
		state1 = not(state1);
		if (state1.equals(state2)) return true;
		if (state1.startsWith("00")) {
			if (("11" + state1.charAt(2)).equals(state2)) {
				return true;
			}
		}
		if (state1.endsWith("00")) {
			if ((state1.charAt(0) + "11").equals(state2)) {
				return true;
			}
		}
		return false;
	}

n*m

n < 5 && m < 1e9

51nod:骨牌覆盖 V2 - 51Nod 1033

计算出第一行的所有情况

在叠加行数,用矩阵快速幂叠加

/**
 * https://vjudge.csgrandeur.cn/problem/51Nod-1033
 * 2个数M N,中间用空格分隔(2 <= m <= 10^9,2 <= n <= 5)
 * ==============================================
 * 参考题解:
 * https://blog.csdn.net/blessLZH0108/article/details/78106856
 */
#include <bits/stdc++.h>
using namespace std;
#define int long long

const int mod = 1e9 + 7;
// n <= 5
int n, m;

// 矩阵乘法
vector<vector<int>> matrixMultiply(const vector<vector<int>>& matrixA,
                                   const vector<vector<int>>& matrixB) {
    int n = matrixA.size();
    int m = matrixA[0].size();
    int p = matrixB[0].size();
    vector<vector<int>> ans(n, vector<int>(p));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < p; k++) {
                ans[i][k] = (ans[i][k] + matrixA[i][j] * matrixB[j][k]) % mod;
            }
        }
    }
    return ans;
}
// 快速幂,此处不考虑0次幂的情况
vector<vector<int>> matrixBinPow(vector<vector<int>> matrix, int k) {
    if (k == 1) {
        return matrix;
    }
    // 迭代法,单位矩阵
    // vector<vector<int>> ans(matrix.size(), vector<int>(matrix[0].size()));
    // for (int i = 0; i < ans.size(); i += 1) {
    //     ans[i][i] = 1;
    // }
    // while (k) {
    //     if (k & 1) {
    //         ans = matrixMultiply(ans, matrix);
    //     }
    //     matrix = matrixMultiply(matrix, matrix);
    //     k >>= 1;
    // }
    // return ans;

    auto ans = matrixBinPow(matrix, k >> 1);
    ans = matrixMultiply(ans, ans);
    if (k & 1) {
        return matrixMultiply(ans, matrix);
    } else {
        return ans;
    }
}

void dfs(vector<vector<int>>& matrix, int col, int pre, int cur) {
    // dp[pre][cur]=1,表示能从pre状态转换到cur状态
    if (col == n) {
        matrix[pre][cur] = 1;
        return;
    }
    //竖放,第col+1列由空变为放骨牌
    dfs(matrix, col + 1, pre << 1, cur << 1 | 1);
    //不放,第col+1列由放骨牌变为空
    dfs(matrix, col + 1, pre << 1 | 1, cur << 1);
    //横放,col+1和col+2列均放骨牌
    if (col + 2 <= n) {
        dfs(matrix, col + 2, pre << 2 | 3, cur << 2 | 3);
    }
}

signed main() {
    cin >> m >> n;
    int mask = 1 << n;
    m += 1;
    
    // 定义dp[][] 位于ij的种类数
    vector<vector<int>> matrix(mask, vector<int>(mask));
    dfs(matrix, 0, 0, 0);

    matrix = matrixBinPow(matrix, m);
    cout << matrix[0][mask - 1] << endl;

    return 0;
}

n*m < 100

杭电:Mondriaan’s Dream- 1400

poj:2411 – Mondriaan’s Dream

52nod:骨牌覆盖 V3 - 51Nod 1034 一直Runtime Error似乎是个历史遗留问题

2411_1.jpg (599×33) (poj.org)

2411_2.jpg (348×316) (poj.org)

n*m < 100 -> min(n, m) <= 10

一行一行处理,每个点三种可能

  • 与上一层 竖放
  • 与前一个 横放
  • 不放
/**
 * https://vjudge.csgrandeur.cn/problem/POJ-2411
 * Mondriaan's Dream
 * 1*2的骨牌摆放n*m
 */
#include <string.h>

#include <iostream>
using namespace std;
#define int long long

// 滚动数组
// 第一位滚动,便于初始化
int dp[2][1 << 12];

signed solve(int n, int m) {
    memset(dp, 0, sizeof(dp));
    // 让m作为较小的一个
    if (n < m) {
        swap(n, m);
    }
    int MASK = 1 << m;

    // 假设处理前的一个状态是满状态的
    dp[0][MASK - 1] = 1;
    int cur = 0;
    // 处理行数
    for (int row = 0; row < n; row += 1) {
        // 处理列,列<行
        for (int col = 0; col < m; col += 1) {
            // 混动数组
            cur ^= 1;
            int pre = cur ^ 1;
            memset(dp[cur], 0, sizeof(dp[cur]));
            for (int mask = 0; mask < MASK; mask += 1) {
                // 前一轮的状态为空,则无法转移
                if (dp[pre][mask] == 0) {
                    continue;
                }

                // 不放,mask<<1让 mask 的最后一位为0
                int change = mask << 1;
                if (change & (1 << m)) {
                    dp[cur][change ^ (1 << m)] += dp[pre][mask];
                }

                // 竖着放,不是第一行,而且上面的位置没放
                if (row && !(mask & (1 << (m - 1)))) {
                    change = (mask << 1) ^ (1 << m) ^ 1;
                    dp[cur][change ^ (1 << m)] += dp[pre][mask];
                }

                // 横着放,不是第一列,且左侧为空
                if (col && !(mask & 1)) {
                    change = (mask << 1) ^ 3;
                    dp[cur][change ^ (1 << m)] += dp[pre][mask];
                }
            }
        }
    }

    cout << dp[cur][MASK - 1] << endl;

    return 0;
}

signed main() {
    int n, m;
    while (cin >> n >> m) {
        if (n == 0 && m == 0) {
            break;
        }
        solve(n, m);
    }

    return 0;
}




END

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

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

相关文章

2022IDEA的下载、安装、配置与使用

文章目录1.IntelliJ IDEA 介绍1.2 JetBrains 公司介绍1.2 IntelliJ IDEA 介绍1.3 IDEA 的主要功能介绍1.3.1 语言支持上1.3.2 其他支持1.4 IDEA 的主要优势&#xff1a;(相较于 Eclipse 而言)1.5 IDEA 的下载地址1.6 官网提供的详细使用文档2.windows 下安装过程2.1 安装前的准…

CANdelaStudio中的状态跳转图无法查看

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;本文将介绍如何解决CANdelaStudio中的状态跳转图无法查看的问题。 参考文章&#xff1a; CDD文件——CANdelaStudio 10服务-DiagnosticSessionControl States To display a graphical representation of the stat…

安装kafka和相关配置解析

1选择操作系统 kafka是一个java应用程序&#xff0c;可以运行在很多系统上Windows, MacOS, Linux, 等 一般都是Linux 2安装java环境 3安装zk zk存储的是metadata 架构图如下 Kafka已经用稳定的zk的3.4.6版本进行了广泛的测试&#xff0c;它可以从apache.org的http://bit.ly/2sD…

摩尔斯电码笔记

文章目录1. 前言2. 什么是摩尔斯电码&#xff1f;3. 摩尔斯电码是如何发明的&#xff1f;4. 摩尔斯电码还在使用吗&#xff1f;5. 如何快速记住6. 体验7. 形式表达8. 相关电影1. 前言 最近突然想起之前学习过的莫斯密码已经忘记了&#xff0c;发现如果不实践的话&#xff0c;就…

OOM和JVM最详细介绍

什么是OOM&#xff1f; OOM&#xff0c;全称“Out Of Memory”&#xff0c;翻译成中文就是“内存用完了”&#xff0c;来源于java.lang.OutOfMemoryError。看下关于的官方说明&#xff1a; Thrown when the Java Virtual Machine cannot allocate an object because it is out…

windows服务器证书算法升级

由于前段时间&#xff0c;服务器检查出证书算法较弱&#xff0c;服务器算法采用的SHA1&#xff0c;这个算法在当前已经较落后了。 第一步在命令端输入certsrv.msc&#xff08;如果打不开&#xff0c;请看我的另一篇文章&#xff0c;先安装服务器证书&#xff09; 第二步打开证…

[LeetCode周赛复盘] 第 91 场双周赛补20221015

[LeetCode周赛复盘] 第 91 场双周赛补20221015 一、本周周赛总结二、 [Easy] 2465. 不同的平均值数目1. 题目描述2. 思路分析3. 代码实现三、[Medium] 2466. 统计构造好字符串的方案数1. 题目描述2. 思路分析3. 代码实现四、[Medium] 2467. 树上最大得分和路径1. 题目描述2. 思…

2.2、物理层下面的传输媒体

2.2、物理层下面的传输媒体 注意&#xff1a; 传输媒体不属于网络体系结构的任何一层。若非要将它添加到体系结构中&#xff0c;那只能放在物理层之下 传输媒体大致分为 2 类 2.2.1、导引型传输媒体 导引型传输媒体 电磁波被导引沿着固体媒体传播常见的导引型传输媒体有同…

电子元器件解析01——电阻

摘要电阻是最基本的电子元器件之一&#xff0c;了解电阻的各方面特性对正确选用合适的电阻很有帮助。本文总结了关于电阻的各个性能参数&#xff0c;包括电阻的标称值、精度、温度系数、耐压、封装与功率&#xff1b;总结了电阻的分类&#xff0c;有多种分类依据&#xff0c;包…

基于jsp+mysql+ssm的校园OTO超市系统-计算机毕业设计

项目介绍 本网站主要是针对高校学生以超市购物为重点开发的网站。系统从用户上分为三种&#xff1a;卖家、买家和游客。系统从模块分为买家模块和卖家模块&#xff0c;买家模块包括用户注册登录、商品浏览、商品详情、商品加入购物车、购物车中商品删除、购物车商品数量变更、…

腾讯代码安全指南开源,涉及 C/C++、Go 等六门编程语言

腾讯代码安全指南旨在梳理 API 层面的风险点并提供详实可操作的编码指引&#xff0c;是我们开展 DevSecOps 安全左移实践探索过程中&#xff0c;梳理沉淀面向开发人员的代码安全参考材料。 本次开源涉及 C/C、JavaScript、Node、Go、Java、Python 六门编程语言的安全指南。 一…

详解风控模型中的逻辑回归评分卡与模型评估内容

今天我们来输出一篇风控长文&#xff0c;关于大家熟悉关注的逻辑评分卡的开发的内容&#xff0c;文章篇幅较长&#xff0c;大纲目录如下&#xff1a; 建模前准备 1.1特征预处理与转化 1.2特征衍生与提取 1.3特征选择与降维 分箱 2.1分箱概述 2.2分箱方法 Woe计算 3.1 WOE 3.2…

【用户画像】功能实现值写入ClickHouse人群包、预估和更新分群人数,NoSQL数据库介绍

文章目录一 写入ClickHouse人群包1 组合查询Bitmap表SQL代码实现&#xff08;1&#xff09;SQL语句分析&#xff08;2&#xff09;实现思路&#xff08;3&#xff09;实现过程controller层service层Taginfo实现类mapper层2 人群包代码实现&#xff08;1&#xff09;配置文件&am…

一个注解干翻所有Controller

1. 概览 日常开发中&#xff0c;最繁琐的便是编写 Controller。很多公司都制定了规范&#xff1a;Controller 不能存在任何的业务逻辑&#xff0c;主要完成参数解析和结果转换。不过查看项目源码&#xff0c;你会发现 Controller 中存在了大量不该存在的逻辑&#xff0c;对此&…

【MFC】打砖块小游戏(下)(7)

任务点&#xff1a; 1、键盘左右键消息处理&#xff1b; 2、碰撞检测&#xff08;与砖块、挡板、上、左、右&#xff09;&#xff1b; 3、控制转向&#xff1b; 程序shix 解决思路&#xff1a; 1、左右键消息处理&#xff1a; 响应 WM_KEYDOWN 消息&#xff0c;移动挡板…

【Spring】——1、使用@Configuration和@Bean给容器中注册组件

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

ArrayList详解

ArrayList是什么? ArrayList就是动态数组&#xff0c;是List接口的可调整大小的数组实现&#xff1b;除了实现List接口之外&#xff0c;该类还提供了一些方法来操纵内部使用的存储列表的数组大小。它的主要底层实现是数组Object[] elementData。 为什么要设计ArrayList&…

【C++】mapset利用红黑树进行简单封装

前言 大家好~~~~呀&#xff01;很荣幸你能点击这篇文章。本篇也是我的一份学习笔记&#xff0c;让我们一起共同成长吧~ing...... C红黑树的简单插入实现博客~ 【C】红黑树的插入实现_柒海啦的博客-CSDN博客 二叉搜索树的基本结构和实现博客~ 【C】二叉搜索树_柒海啦的博客-CSDN…

java 实现一个最小栈

文章目录最小栈1.实现思路2.实现过程演示3.代码实现思路3.1 压入思路3.2 弹出思路3.3 如何返回栈顶元素的下标3.4 如何返回栈的最小值4.整体代码实现最小栈 1.实现思路 实现一个stack栈 和 minStack栈。先将数据一个一个压入到 stack 中。找到 stack 中的最小值。minStack中始…

简单介绍动态链接过程

文章目录gotgot[0] link_map结构体地址got[1] _dl_runtime_resolvegot[2]之后pltplt[0] 调用libc解析函数plt后面的plt.sec随便拿ida打开一个程序可以看到这是got的内容gdb一下查看内容&#xff0c;可以看到地址是从0开始的大家也知道 got是个独立的section&#xff0c;所以最开…