将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。
现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
请编程对给出的棋盘及n,求出均方差的最小值。
输入格式
第1行为一个整数n。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出格式
输出最小均方差值(四舍五入精确到小数点后三位)。
数据范围
1<n<15
输入样例:
3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3
输出样例:
1.633
这道题目是来自1999 年的NOI,虽然是NOI, 但是由于年份久远,是可以做的(02 说要克服心里的恐惧)。
思路:首先这道题目的大体做法要搞清楚,刚看到这道题目,虽然是区间dp专题,但是也搞不懂该怎么做。
其实我写下来的目的是想搞明白这道题目为什么可以用区间dp来做,区间dp的限制是什么?
其实我想的是, 首先能不能用区间dp的主要的因素是分成的两个区间与主区间存不存在一定的联系, 区间分成的两部分之间的关系和融合为一起的区间的联系或者是加在一起, 或者是乘在一起, 就像本题目一样, 两个分成的区间的最小方差加在一起难道就是总区间的最小值了吗,当然在知道题解后, 答案是肯定的, 但是为什么呢, 我们总要问一下, 这样我们才能进步, 可能从这道题目的收获最大并不是知道这道题目怎么做, 而是这道题目启发了你什么, 要怎么样的总结,那么回归正题, 这道题目为什么可以这样做呢。
推导了一下, 发现只要满足切割成的部分, 那么这样就能很好的解释了为什么两个分区间的最大值的和等于总区间的最大值了, 并且满足相加性。
对应着代码有两种方式, 一个是记忆化搜索, 这个是推荐的解法, 另一种是直接暴力循环, 这样也可以, 思维会稍微难点(也没难多少啦)。
有点长, 复习的话大概看看就好了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 9, M = 16, INF = 0x3f3f3f3f;
int s[N][N], a[N][N];
int f[M][N][N][N][N];
int get(int x1, int y1, int x2, int y2)
{
int t = s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
return t * t;
}
int dfs(int x1, int y1, int x2, int y2, int k)
{
int &v = f[k][x1][y1][x2][y2];
if(v != -1) return v;
if(k == 0) return v = get(x1, y1, x2, y2);
v = INF;
for(int i = x1; i < x2; i ++)
{
v = min(v, dfs(x1, y1, i, y2, k - 1) + f[0][i + 1][y1][x2][y2]);
v = min(v, dfs(i + 1, y1, x2, y2, k - 1) + f[0][x1][y1][i][y2]);
//min(f[k - 1][x1][y1][i][y2] + f[0][i + 1][y1][x2][y2], f[k - 1][i + 1][y1][x2][y2] + f[0][x1][y1][i][y2])
}
for(int i = y1; i < y2; i ++)
{
v = min(v, dfs(x1, y1, x2, i, k - 1) + f[0][x1][i + 1][x2][y2]);
v = min(v, dfs(x1, i + 1, x2, y2, k - 1) + f[0][x1][y1][x2][i]);
// min(f[k - 1][x1][y1][x2][i] + f[0][x1][i + 1][x2][y2], + f[k - 1][x1][i + 1][x2][y2])
}
return v;
}
int main()
{
int n; cin >> n;
for(int i = 1; i <= 8; i ++)
for(int j = 1; j <= 8; j ++)
{
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
memset(f, -1, sizeof f);
for(int x1 = 1; x1 <= 8; x1 ++)
for(int y1 = 1; y1 <= 8; y1 ++)
for(int x2 = x1; x2 <= 8; x2 ++)
for(int y2 = y1; y2 <= 8; y2 ++)
{
int t = get(x1, y1, x2, y2);
f[0][x1][y1][x2][y2] = t;
}
dfs(1, 1, 8, 8, n - 1);
double ave = (double)s[8][8] / n;
double ans = sqrt(((double)f[n - 1][1][1][8][8] / n - ave * ave));
printf("%.3lf", ans);
return 0;
}
还有短的!!
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 9, M = 16, INF = 0x3f3f3f3f;
int s[N][N], a[N][N];
int f[M][N][N][N][N];
int get(int x1, int y1, int x2, int y2)
{
return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
int main()
{
int n; cin >> n;
for(int i = 1; i <= 8; i ++)
for(int j = 1; j <= 8; j ++)
{
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
for(int x1 = 1; x1 <= 8; x1 ++)
for(int y1 = 1; y1 <= 8; y1 ++)
for(int x2 = x1; x2 <= 8; x2 ++)
for(int y2 = y1; y2 <= 8; y2 ++)
{
int t = get(x1, y1, x2, y2);
f[0][x1][y1][x2][y2] = t * t;
}
for(int k = 1; k < n; k ++)
for(int x1 = 1; x1 <= 8; x1 ++)
for(int y1 = 1; y1 <= 8; y1 ++)
for(int x2 = x1; x2 <= 8; x2 ++)
for(int y2 = y1; y2 <= 8; y2 ++)
{
int minv = INF;
for(int i = x1; i < x2; i ++)
{
minv = min(minv, min(f[k - 1][x1][y1][i][y2] + f[0][i + 1][y1][x2][y2], f[k - 1][i + 1][y1][x2][y2] + f[0][x1][y1][i][y2]));
}
for(int i = y1; i < y2; i ++)
{
minv = min(minv, min(f[k - 1][x1][y1][x2][i] + f[0][x1][i + 1][x2][y2], f[0][x1][y1][x2][i] + f[k - 1][x1][i + 1][x2][y2]));
}
f[k][x1][y1][x2][y2] = minv;
}
double ave = (double)s[8][8] / n;
double ans = sqrt(((double)f[n - 1][1][1][8][8] / n - ave * ave));
printf("%.3lf", ans);
return 0;
}