本文来自我的洛谷博客

这个题解思路虽然与其他人的思路相同,

但力求使用清晰易懂的图片和文字,讲解最简洁的道理。

请大家耐心地看完,注意要结合图片一起哦~~

2022-8-24 更改了格式与错别字。

2022-8-28 更改了数学公式格式。

这是本蒟蒻第一次写题解,不足之处请多包涵。


题目大意:

读完题的可以跳过这一部分。

给定一个矩阵,每个位置上都有数字。

可以分割 \(n-1\) 次,每次分割为 \(2\) 个矩形,然后把一半放在一旁,然后在另外一半继续割。

像这样:

可以横切也可以纵切。

样例给的很好。

然后就分为 \(n\) 块(因为割了 \(n-1\) 次)。

记 \(X=\dfrac{s}{n}\),\(s\) 为矩阵中所有的数字之和。

设第 \(i\) 块的和为 \(x_i\),那么求出怎样割才能使 $\sum_{i=1}{n}(x_i-X)2 $ 更小。


分析问题:

我们看到这种分割问题,最后组合起来求总体最优值,便可以立马联想到区间 DP。这叫望梅止渴做 DP 问题的复杂反射。

毕竟区间 DP 的主要思想就是大区间包含小区间,

小区间汇集成大区间。

好了,废话不多说,我们先从如下几个角度思考:

  • 状态表示
  • 状态含义
  • 目标状态
  • 状态转移

一、状态表示:\(f(x1,y1,x2,y2,k)\)。

二、状态含义:\(f(x1,y1,x2,y2,k)\) 表示求解子矩阵 \((x1,y1)\sim(x2,y2)\) 割了 \(k\) 刀得来的最优解(即下图框住区域的最优解)。

三、目标状态:\(f(1,1,8,8,n)\),即求解整个矩阵被割了 \(n\) 刀的最优解。

四、状态转移:

我们以下图为例,讲解 \(f(x1,y1,x2,y2,k)\) 是如何被拆分的。

①:考虑选择上面继续割(如下图),丢掉下面的,其分界线为第 \(i\) 行。

所以应该取上面的最优值,同时少割一刀:\(f(x1,y1,i,y2,k-1)\),

而下面的部分为定值:\(\dfrac{(sum-X)^2}{n}\)。

\(sum\) 为下面的部分所有格子的和。

这两个部分合起来就是 \(f(x1,y1,x2,y2,k)\)。


②:考虑选择下面继续割(如上图)。

上面部分的定值:\(\dfrac{(sum-X)^2}{n}\)。

下面的最优值:\(f(i+1,y1,x2,y2,k-1)\)。

\(sum\) 为上面的部分所有格子的和。


下面考虑纵切。

③:考虑选择左边继续割(如上图),分界线为第 \(i\) 列。

取左边的最优值:\(f(x1,y1,x2,i,k-1)\),

右边的部分为定值:\(\dfrac{(sum-X)^2}{n}\)。

\(sum\) 为右边的部分所有格子的和。


④:考虑选择右边继续割(如上图)。

取右边的最优值:\(f(x1,i+1,x2,y2,k-1)\),

左边的部分为定值:\((sum-X)\times(sum-X)/n\),

\(sum\) 为左边的部分所有格子的和。


我们每次取一个值,其实都是在将问题规模缩小。

情况考虑清楚了,那怎么从一个 \(f\) 到另一个 \(f\) 呢,如果是用普通的区间 DP,那估计要使用 \(5\) 层甚至更多的循环,所以,我们使用万能的记忆化搜索,免去繁琐的循环结构。


综上所述,

我们便实现了对大区间的拆分。

而我们不断提到 \(sum\),是一块区域的和,那么,我们可以使用二维前缀和来维护。相信大家一定会。

好了,上 AC 代码。

#include <bits/stdc++.h>

using namespace std;

const int N=15;
const double INF=1e10; //因为要求min,所以要定义INF int n;
int m=8;
double X; //平均值
double s[N][N]; //记录每个格子的值
double f[N][N][N][N][N]; //状态 double GetSum(int x1,int y1,int x2,int y2)//求[x1,y1]~[x2,y2]的和,为下文的GetX服务
{
return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
} double GetX(int x1,int y1,int x2,int y2)// 计算上文的(sum−X)×(sum−X)/n。
{
return (GetSum(x1,y1,x2,y2)-X)*(GetSum(x1,y1,x2,y2)-X)/n;
} double DFS(int x1,int y1,int x2,int y2,int k)//使用记忆化搜索进行递归调用
{
double& v=f[x1][y1][x2][y2][k];//因为太难写了,所以给f[x1][y1][x2][y2][k]建立引用
if(v>=0)return v; //已经访问过该点了,直接返回
if(k==1)return v=GetX(x1,y1,x2,y2);//最后一块,不可能再割了 v=INF; //为求最小值做准备 for(int i=x1;i<x2;i++) //下面是刚刚讨论的结果
{
v=min(v,DFS(x1,y1,i,y2,k-1)+GetX(i+1,y1,x2,y2));
v=min(v,DFS(i+1,y1,x2,y2,k-1)+GetX(x1,y1,i,y2));
} for(int i=y1;i<y2;i++)
{
v=min(v,DFS(x1,y1,x2,i,k-1)+GetX(x1,i+1,x2,y2));
v=min(v,DFS(x1,i+1,x2,y2,k-1)+GetX(x1,y1,x2,i));
} return v;
} int main()
{
scanf("%d",&n);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
double x;
scanf("%lf",&x);
s[i][j]=s[i-1][j]+s[i][j-1]+x-s[i-1][j-1]; //建立前缀和
}
}
X=s[m][m]/n; //求平均值
memset(f,0x80,sizeof f); //初始化
printf("%.3f\n",sqrt(DFS(1,1,m,m,n)));//注意,一定要根号啊啊啊!!!
return 0;
}

P5752 [NOI1999] 棋盘分割题解的更多相关文章

  1. [NOI1999] 棋盘分割

    COGS 100. [NOI1999] 棋盘分割 http://www.cogs.pro/cogs/problem/problem.php?pid=100 ★★   输入文件:division.in  ...

  2. POJ1991 NOI1999棋盘分割

    棋盘分割 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 15581   Accepted: 5534 Description ...

  3. [NOI1999] 棋盘分割(推式子+dp)

    http://poj.org/problem?id=1191 棋盘分割 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 156 ...

  4. 【Luogu】P1436 棋盘分割 题解

    嗯,点开题目,哇!是一道闪亮亮的蓝题! 不要被吓到了,其实,这道题就是一个简单的DP啦! 我们设 \(f[x1][y1][x2][y2][c]\) 为以 \((x1,y1)\) 为左上角,以 \((x ...

  5. POJ 1191 棋盘分割

    棋盘分割 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 11213 Accepted: 3951 Description 将一个 ...

  6. poj 1191 棋盘分割 动态规划

    棋盘分割 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 11457   Accepted: 4032 Description ...

  7. NOI 193棋盘分割.cpp

    193:棋盘分割 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分 ...

  8. HDU 2517 / POJ 1191 棋盘分割 区间DP / 记忆化搜索

    题目链接: 黑书 P116 HDU 2157 棋盘分割 POJ 1191 棋盘分割 分析:  枚举所有可能的切割方法. 但如果用递归的方法要加上记忆搜索, 不能会超时... 代码: #include& ...

  9. POJ 1191棋盘分割问题

    棋盘分割问题 题目大意,将一个棋盘分割成k-1个矩形,每个矩形都对应一个权值,让所有的权值最小求分法 很像区间DP,但是也不能说就是 我们只要想好了一个怎么变成两个,剩下的就好了,但是怎么变,就是变化 ...

  10. 洛谷 P1436 棋盘分割 解题报告

    P1436 棋盘分割 题目描述 将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的两部分中的任意一块继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共 ...

随机推荐

  1. CI框架内置分页代码

    Controller 控制器代码 <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welc ...

  2. 从零开始配置深度学习环境:CUDA+Anaconda+Pytorch+TensorFlow

    本文适用于电脑有GPU(显卡)的同学,没有的话直接安装cpu版是简单的.CUDA是系统调用GPU所必须的,所以教程从安装CUDA开始. CUDA安装 CUDA是加速深度学习计算的工具,诞生于NVIDI ...

  3. Cron表达式介绍与示例

      1. 概念介绍 Cron表达式是一个具有时间含义的字符串,字符串以5~6个空格隔开,分为6~7个域,格式为X X X X X X X.其中X是一个域的占位符.最后一个代表年份的域非必须,可省略.单 ...

  4. 任务拆解,悠然自得,自动版本的ChatGPT,AutoGPT自动人工智能AI任务实践(Python3.10)

    当我们使用ChatGPT完成某些工作的时候,往往需要多轮对话,比如让ChatGPT分析.翻译.总结一篇网上的文章或者文档,再将总结的结果以文本的形式存储在本地.过程中免不了要和ChatGPT" ...

  5. 快速上手Linux核心命令(五):文本处理三剑客

    @ 目录 前言 正则表达式 第一剑客 grep 第二剑客 sed 第三 剑客 awk 小结 剑仙镇楼~ O(∩_∩)O 前言 上一篇中已经预告,我们这篇主要说Linux文本处理三剑客.他们分别是gre ...

  6. rfc7234之http缓存

    声明:本人原创文章,详细内容已发布在我的微信个人技术公众号---网络技术修炼,公众号总结普及网络基础知识,包括基础原理.网络方案.开发经验和问题定位案例等,欢迎关注. 缓存概念 缓存处理请求步骤 缓存 ...

  7. 【Docker】容器管理

    一.容器生命周期及启动过程 1.容器生命周期 2.容器启动过程 二.容器管理命令 Usage: docker [OPTIONS] COMMAND A self-sufficient runtime f ...

  8. MySQL-分组函数ROLLUP的基本用法

    一.ROLLUP简介 ROLLUP是GROUP BY子句的扩展. ROLLUP选项允许包含表示小计的额外行,通常称为超级聚合行,以及总计行. 通过使用ROLLUP选项,可以使用单个查询生成多个分组集. ...

  9. 【转】内存清零KILL进程

    #include <Windows.h> #include <Ntsecapi.h> #include <Aclapi.h> #include <tlhelp ...

  10. 2022-12-21:uifd/ui-for-docker是docker的web可视化工具。请问部署在k3s中,yaml文件如何写?

    2022-12-21:uifd/ui-for-docker是docker的web可视化工具.请问部署在k3s中,yaml文件如何写? 答案2022-12-21: yaml如下: apiVersion: ...