AcWing 321 棋盘分割

谁都会走 提交于 2020-02-19 13:33:00

题目描述:

将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)

1191_1.jpg

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。

现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。

均方差formula.png ,其中平均值lala.png ,xixi为第 i 块矩形棋盘的总分。

请编程对给出的棋盘及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

分析:

本题是二维区间DP问题。应该说本题的题意表述不太清楚,只能沿着棋盘格子的边进行切割这句话太抽象,题目想要表达的意思是对8*8的棋盘,横着切一刀或者竖着切一刀,切完后选择留其中的一块继续切割,另一块就不能再进行切割了,一共需要切n-1刀,分割成n个矩形,要求如何切分得这n个矩形的均方差最小,最小的均方差是多少?

一维的区间dp问题往往用f[l,r]来表示状态,二维的自然要用f[x1,y1,x2,y2]表示矩形区间的状态了,由于有块数限制,所以还要增加一维k表示该块区域需要分成多少块。故状态表示f[x1][y1][x2][y2][k]表示在左上角坐标为(x1,y1),右下角坐标为(x2,y2)的矩形区域内切成k块矩形的最小均方差。由于均方差需要开根号不方便,可以直接求在矩形区域内的最小方差。即(xi - x')^2 / k最小。对于矩形区域,我们需要考虑下一刀是横着且还是竖着切,切完留哪一块丢掉哪一块。

如图所示,竖着切一刀,左边的矩形是(x1,y1)到(i,y2),右边的矩形是(i+1,y1)到(x2,y2),如果选择留左边的那块,f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k],f[x1][y1][i][y2][k-1] + S)其中S为右边那块的方差,由于状态维数较多,使用循环层数也很多,所以这里使用记忆化搜索来做。由于f数组是double类型,在memset(f,-1,sizeof f)时是对浮点数的每一位都初始化为1,根据IEEE754标准,相当于将f初始化为负无穷,在记忆化搜索时需要判断f >= 0则说明已经搜索过该状态了,但是不可写成f != -1,尽管初始化int类型数组时memset是可以直接将数组初始化为-1的,注意其中的区别。具体的实现细节见代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 10,M = 16;
int n;
double X,s[N][N],f[N][N][N][N][M];
double get(int x1,int y1,int x2,int y2){
    return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}
double dfs(int x1,int y1,int x2,int y2,int k){
    double &v = f[x1][y1][x2][y2][k];
    if(v >= 0)  return v;
    if(k == 1){
        double s = get(x1,y1,x2,y2) - X;
        return v = s * s / n;
    }
    v = 1e9;
    for(int i = x1;i < x2;i++){
        double s1 = get(i + 1,y1,x2,y2) - X,s2 = get(x1,y1,i,y2) - X;
        v = min(v,dfs(x1,y1,i,y2,k-1) + s1 * s1 / n);
        v = min(v,dfs(i + 1,y1,x2,y2,k-1) + s2 * s2 / n);
    }
    for(int i = y1;i < y2;i++){
        double s1 = get(x1,i + 1,x2,y2) - X,s2 = get(x1,y1,x2,i) - X;
        v = min(v,dfs(x1,y1,x2,i,k-1) + s1 * s1 / n);
        v = min(v,dfs(x1,i + 1,x2,y2,k-1) + s2 * s2 / n);
    }
    return v;
}
int main(){
    cin>>n;
    for(int i = 1;i <= 8;i++){
        for(int j = 1;j <= 8;j++){
            cin>>s[i][j];
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
        }
    }
    X = s[8][8] / n;
    memset(f,-1,sizeof f);
    printf("%.3lf\n",sqrt(dfs(1,1,8,8,n)));
    return 0;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!