前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >棋盘分割

棋盘分割

作者头像
小K算法
发布于 2022-11-24 08:02:50
发布于 2022-11-24 08:02:50
64300
代码可运行
举报
文章被收录于专栏:小K算法小K算法
运行总次数:0
代码可运行

作者 | 小K

出品 | 公众号:小K算法

01

故事起源

有一个8*8的棋盘,沿着格子的边进行分割,每分割一块拿走,将剩下的矩形棋盘继续分割。

n-1次分割后,可以得到n块矩形棋盘。

假设原棋盘每一格都有一个分值,则分割后的每一块都有一个总分,总分即为所有格子分值之和。

设分割的每一块棋盘总分为xi。

如何分割可以让各矩形棋盘总分的均方差最小?

02

分析

先对均方差公式作一些简化。

通过上面公式可以看出,其实就是要求分割后的n个矩形棋盘,分值平方的总和最小。

当你遇到一些没有头绪的问题的时候,先不要想太多,别上来整什么高大上的思想,咱们就从最简单的场景开始分析。

2.1

缩小问题规模

假设棋盘规模不是8*8,而是2*2。缩小问题规模是一种很有用的方法,因为问题的本质并没有发生变化,只是数据更小,就更有利于我们分析问题的本质。

2.2

开始分割

这时再让你分割,还会觉得难吗,全部手动枚举也能解决啊。

先垂直切。

先水平切。

切2次,分割成3块,总共也就4种情况,把每一种情况的方差算出来,选一个最小的就行了。

2.3

规律

每次分割有2种决策,要么水平切,要么垂直切。

针对一种决策,又有很多种具体的不同的切法。例如一个4*4的棋盘,先垂直切,就可以从3个不同的位置切。

如果给棋盘加一个坐标,每一种切法就可以用一个线段来具体表示,比如用这条切线的两个端点坐标。

分割之后,就变成了2个更小的棋盘,而这2个棋盘也可以用矩形的坐标表示,此时就把原问题变成了子问题,原问题的最优解也就是所有子问题中选一个最优的。

此时你可能会有几个疑问:

1.为什么全局最优解可以转化为子问题的最优解呢? 上面的分割方法,在每一个阶段,我们已经把所有可能的分割方法全部枚举完了,那其中的最优肯定就是当前阶段的最优了,因为没有其它的可能性。

2.为什么当前阶段的最优可以转化为下一阶段? 这就是一个无后效性,因为我们只需要分割的分值平方和最小,并不关心它具体是怎么分割的。之前怎么分割,在当前阶段看来都是一样的,不受影响。

3.应该用几维来表示状态呢?这个就是到底要开几维数组来递推。上面无论是表示分割的线段,还是分割后的矩形,都至少要2个点坐标,所以至少得4维。

03

建模

先垂直切,继续切左边或者右边,找出最优解。

先水平切,继续切上边或者下边,找出最优解。

当前轮次t只与上一次t-1有关,所以可以用01滚动数组,压缩一维。

初始化的时候,所有小块的独立矩形棋盘都一次不切,所以就是矩形棋盘的分值平方。

04

代码实现

定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define N 9
int chessboard[N][N];
int d[2][N][N][N][N];
int sum[N][N] = {0};

int getSum(int x, int y, int k, int l) {
    int ans = sum[k][l] - sum[k][y - 1] - sum[x - 1][l] + sum[x - 1][y - 1];
    return ans * ans;
}

int min(int x, int y) {
    return x < y ? x : y;
}

初始化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void init() {
  for (int i = 1; i <= 8; ++i) {
        for (int j = 1; j <= 8; ++j) {
            cin >> chessboard[i][j];
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + chessboard[i][j];
        }
    }
    for (int i = 1; i < N; ++i) {
        for (int j = 1; j < N; ++j) {
            for (int k = i; k < N; ++k) {
                for (int l = j; l < N; ++l) {
                    d[0][i][j][k][l] = getSum(i, j, k, l);
                }
            }
        }
    }
}

main

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main() {
    int n;
    cin >> n;
    init();
    int s = 0;
    for (int t = 1; t < n; ++t) {
        s = 1 - s;
        // 枚举起点
        for (int i = 1; i < N; ++i) {
            for (int j = 1; j < N; ++j) {
                // 枚举终点
                for (int k = i; k < N; ++k) {
                    for (int l = j; l < N; ++l) {
                        d[s][i][j][k][l] = 1e8;
                        // 横切
                        for (int a = i; a < k; ++a) {
                            d[s][i][j][k][l] = min(d[s][i][j][k][l], d[1 - s][i][j][a][l] + getSum(a + 1, j, k, l));
                            d[s][i][j][k][l] = min(d[s][i][j][k][l], d[1 - s][a + 1][j][k][l] + getSum(i, j, a, l));
                        }
                        // 纵切
                        for (int b = j; b < l; ++b) {
                            d[s][i][j][k][l] = min(d[s][i][j][k][l], d[1 - s][i][j][k][b] + getSum(i, b + 1, k, l));
                            d[s][i][j][k][l] = min(d[s][i][j][k][l], d[1 - s][i][b + 1][k][l] + getSum(i, j, k, b));
                        }
                    }
                }
            }
        }
    }
    double average = sum[N - 1][N - 1] * 1.0 / n;
    double ans = d[s][1][1][8][8] * 1.0 / n - average * average;
    printf("%.3lf", sqrt(ans));
    return 0;
}

05

总结

动态规划最重要的就是找出阶段、状态和决策,有时可以先用递归的方式建模,然后加记忆化优化,最后再转为递推。

本文原创作者:小K,一个思维独特的写手。 文章首发平台:微信公众号【小K算法】。

如果喜欢小K的文章,请点个关注,分享给更多的人,小K将持续更新,谢谢啦!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小K算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
POJ-1191-棋盘分割(动态规划)
棋盘分割 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 13593 Accepted: 4846 Description 将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行) 原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在
ShenduCC
2018/04/25
9180
acwing321. 棋盘分割(动态规划+记忆化搜索)「建议收藏」
将一个 8×8 的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了 (n−1) 次后,连同最后剩下的矩形棋盘共有 n 块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
全栈程序员站长
2022/09/22
2030
3.算法设计与分析__分治法
将一个难以直接解决的大问题,划分成一些规模较小的子问题,以便各个击破,分而治之。更一般地说,将要求解的原问题划分成k个较小规模的子问题,对这k个子问题分别求解。如果子问题的规模仍然不够小,则再将每个子问题划分为k个规模更小的子问题,如此分解下去,直到问题规模足够小,很容易求出其解为止,再将子问题的解合并为一个更大规模的问题的解,自底向上逐步求出原问题的解。
Twcat_tree
2022/11/30
7870
3.算法设计与分析__分治法
区间dp入门_状压dp
j~ends堆合并 = 较小的(原来, 分割点i坐部分重量 + 分割点i右边部分重量 + 合并后两堆总重量)
全栈程序员站长
2022/10/05
6820
区间dp入门_状压dp
分治(详解残缺棋盘 —— Java代码实现)
@toc 分治 总体思想 将要求解的较大规模的问题分割成k个更小规模的子问题 对这k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k为子问题,如此递归进行下去,直到问题规模足够小,很容易求出其解为止 将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来的问题的解 使用条件 该问题的规模缩小到一定的程度就可以容易地解决 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质 利用该问题分解出的子问题的解可以合并为该问题的解 该问题所分解出的各个子问题是相互独立的,
ruochen
2021/05/14
8620
分治(详解残缺棋盘 —— Java代码实现)
Greedy & Violent
1 2 3//坐标缩小后就可以更方便的选择 double pos = (double)i / n * (n + m);//原来雕像的位置 ans += fabs(pos - floor(pos + 0.5))/(n + m);//*n+m后就选四舍五入最近的
radaren
2018/08/28
5570
java 实现棋盘覆盖问题
问题描述:在一个2k*2k的棋盘中,有一个特殊方格,要求用L型骨牌覆盖满除特殊方格外的所有其他方格,且骨牌不得重叠.(骨牌可以旋转放置) 输入:棋盘的边长、特殊方格坐标 输出:骨牌放法.其中用0表示特殊方格,同一张骨牌所占方格用同一个数字表示,不同骨牌用不同数字.  解题思想: 采用分治法解决该问题。分治法是把一个规模很大的问题分解为多个规模较小、类似的子问题,然后递归地解决所有子问题,最后再由子问题的解决得到原问题的解决。 所以将2k*2k的棋盘,先分成相等的四块子棋盘,其中特殊方格位于四个中的一个,构造
yawn
2018/03/14
1.8K0
java 实现棋盘覆盖问题
棋盘覆盖问题
Tags: 算法 棋盘覆盖问题 ---- 【问题描述】 在一个2^k×2^k个方格组成的棋盘中,若有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一个特殊棋盘.显然特殊方格在棋盘上出现
zhwhong
2018/05/16
3.2K0
算法-经典趣题-马踏棋盘(又称骑士周游)
马踏棋盘问题,又称骑士漫步、,它是一个非常有趣的智力问题。马踏棋盘问题的大意如下:
joshua317
2021/09/10
2.3K0
算法-经典趣题-马踏棋盘(又称骑士周游)
棋盘覆盖问题(Java)
在一个2k×2k个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一个特殊棋盘。显然特殊方格在棋盘上出现的位置有4k 种情形.因而对任何k ≥ 0,有4k种不同的特殊棋盘。如下图中的特殊棋盘是当k = 2时16个特殊棋盘中的一个。
WHYBIGDATA
2023/01/31
7780
棋盘覆盖问题(Java)
2023-09-05:请用go语言编写。一个图像有n个像素点,存储在一个长度为n的数组arr里, 每个像素点的取值范围[0,s]
2023-09-05:请用go语言编写。一个图像有n个像素点,存储在一个长度为n的数组arr里,
福大大架构师每日一题
2023/09/06
2100
2023-09-05:请用go语言编写。一个图像有n个像素点,存储在一个长度为n的数组arr里, 每个像素点的取值范围[0,s]
程序员进阶之算法练习(四十八)LeetCode
题目链接 题目大意: 给出两个非空的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
落影
2020/10/27
4150
程序员进阶之算法练习(四十八)LeetCode
LeetCode 688. “马”在棋盘上的概率(DP)
已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。
Michael阿明
2021/02/19
5990
程序员进阶之算法练习(八十八)- CF883
题目链接 题目大意: 在地面上有n个点,点都在同一条垂直地面的线上面,每个点的高度为a[i]; 每个点有一条绳子,绳子长度为b[i],一端连着点,一端连着小球(每个点相连的小球是同一个);
落影
2023/10/23
1700
程序员进阶之算法练习(八十八)- CF883
基于生长的棋盘格角点检测方法--(3)代码详解(下)
接着上一篇基于生长的棋盘格角点检测方法–(2)代码详解(上),来看一下第二个重要函数chessboardsFromCorners。 该函数的目的是用上一步骤中找到的角点恢复出棋盘结构。首先初始化一
用户1150922
2018/01/08
2K0
基于生长的棋盘格角点检测方法--(3)代码详解(下)
程序员进阶之算法练习(六)
前言 这次只有四个题目,E题是个奇奇怪怪的数学题,就不去啃这个硬骨头了,我们来分析下A/B/C/D: A题是简单的找规律题; B题是博弈的入门题; C题是简单的模拟题,题目较长; D题是贪心,也可以说是构造。 看完题目大意,先思考,再看解析;觉得题目大意不清晰,点击题目链接看原文。 文集: 程序员进阶之算法练习(一) 程序员进阶之算法练习(二) 程序员进阶之算法练习(三) 程序员进阶之算法练习(四) 程序员进阶之算法练习(五) 代码地址 A 题目链接 题目大意:输入n,输出一个字符串。
落影
2018/04/27
6680
算法--枚举策略
枚举法的基本思想 枚举法的基本思想是根据提出的问题枚举所有可能状态,并用问题给定的条件检验哪些是需要的,哪些是不需要的。能使命题成立,即为其解。 枚举结构:循环+判断语句。  枚举法的条件 虽然枚举法本质上属于搜索策略,但是它与后面讲的回溯法有所不同。因为适用枚举法求解的问题必须满足两个条件: ⑴可预先确定每个状态的元素个数n; ⑵状态元素a1,a2,…,an的可能值为一个连续的值域。 枚举法的框架结构 设ai1—状态元素ai的最小值;aik—状态元素ai的最大值(1≤i≤n),即a11≤a1≤a1k,a2
Angel_Kitty
2018/04/08
1.4K0
算法--枚举策略
干货|十分钟教你用动态规划算法解Travelling Salesman Problem(TSP)问题,附代码……
乍一看标题,大家是不是觉得“动态规划”这四个字组合在一起有点眼熟?似乎哪会儿学过来着……但是吧,细细一琢磨,又忘了它具体是什么、怎么用、用来解决哪些问题了。 莫方,小编出现就是为了解决大家一切在学(zhuang)习(bi)上的需求的。动态规划忘了是吧,那今天小编就陪你好好回忆一下。 什么是TSP和动态规划 简单来说,Travelling Salesman Problem (TSP) 是最基本的路线问题。它寻求的是旅行者由起点出发,通过所有给定的需求点后,再次返回起点所花费的最小路径
用户1621951
2018/04/19
28.9K2
干货|十分钟教你用动态规划算法解Travelling Salesman Problem(TSP)问题,附代码……
P1169「棋盘制作」
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个 大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。
hotarugali
2022/03/01
6240
P1169「棋盘制作」
马踏棋盘 - plus studio
这段代码使用一个while循环来控制马的移动,直到访问了棋盘上的所有格子(move_count达到SIZE * SIZE)或者无法找到合适的下一步移动位置。
plus sign
2024/02/29
950
推荐阅读
相关推荐
POJ-1191-棋盘分割(动态规划)
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文