zoukankan      html  css  js  c++  java
  • [POJ 1911] 棋盘

    问题描述

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

    img

    原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
    均方差 img,其中平均值 img,x i为第i块矩形棋盘的总分。
    请编程对给出的棋盘及n,求出O'的最小值。

    输入格式

    第1行为一个整数n(1 < n < 15)。
    第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。

    输出格式

    仅一个数,为O'(四舍五入精确到小数点后三位)。

    样例输入输出

    样例输入

    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

    解析

    首先,我们需要将均方差的公式进行一定的变形,方便进行动态规划。公式变形如下:

    [egin{align} sigma &= sqrt{frac{sum_{i=1}^n{(x_i-overline{x})^2}}{n}}\ &= sqrt{frac{sum_{i=1}^n{(x_i^2-2x_ioverline{x}+overline{x}^2)}}{n}}\ &= sqrt{frac{sum_{i=1}^n{x_i^2}-2overline{x}sum_{i=1}^nx_i+noverline{x}}{n}}\ &= sqrt{frac{sum_{i=1}^n{x_i^2}}{n}-overline{x}^2} end{align} ]

    那么,现在只与每个矩形的元素和的平方有关。矩阵的和可以用二位前缀和的方式来解决,下面的关键是如何用动态规划的方式解决这个问题。想要描述一个状态,显然需要知道当前剩余矩形的位置。另外,由于受切割次数的限制,还需要记录这个矩形是割了几次后的结果。由此,我们有如下动态规划策略:

    (f[i][j]][k][l][d])表示在切割了d次后剩余的矩形左上角为(i,j)、右上角为(k,l)时的最优解。那么转移时可以由题目要求,从各个方向进行转移。方程因为太长,在代码里注释。代码里将(f[i][j][k][l][0])设为矩形元素和的平方。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    const int INF=1<<30;
    int n, chess[9][9]={0}, sum[9][9]={0}, dp[9][9][9][9][15]={0};
    //直接计算矩形(y1, x1)(y2, x2)矩形分数平方
    int getX(int y1, int x1, int y2, int x2){
        int a=sum[y2][x2]-sum[y2][x1-1]-sum[y1-1][x2]+sum[y1-1][x1-1];
        return a*a;
    }
    int main(){
        scanf("%d", &n);
        //统一i表示y,j表示x
        for(int i=1;i<=8;i++)
            for(int j=1;j<=8;j++)
                scanf("%d", &chess[i][j]);
        //计算sum数组(矩形(1, 1)(i, j)的分数和),方便直接计算getX
        for(int i=1;i<=8;i++){
            for(int j=1;j<=8;j++)
                sum[i][j]=sum[i][j-1]+chess[i][j];
            for(int j=1;j<=8;j++)
                sum[i][j]+=sum[i-1][j];
        }
        //初值
        for(int i1=1;i1<=8;i1++)
            for(int j1=1;j1<=8;j1++)
                for(int i2=i1;i2<=8;i2++)
                    for(int j2=j1;j2<=8;j2++)
                        dp[i1][j1][i2][j2][0]=getX(i1, j1, i2, j2);
        //这里的i是切割数(分析里的d)
        for(int i=1;i<n;i++)
            for(int i1=1;i1<=8;i1++)
                for(int j1=1;j1<=8;j1++)
                    for(int i2=i1;i2<=8;i2++)
                        for(int j2=j1;j2<=8;j2++){
                            //赋值INF,若状态不合法不会干扰其他状态
                            dp[i1][j1][i2][j2][i]=INF;
                            //左右切割
                            for(int k=j1;k<j2;k++)
                                dp[i1][j1][i2][j2][i]=min(dp[i1][j1][i2][j2][i], min(dp[i1][j1][i2][k][i-1]+dp[i1][k+1][i2][j2][0], dp[i1][j1][i2][k][0]+dp[i1][k+1][i2][j2][i-1]));
                            //上下切割
                            for(int k=i1;k<i2;k++)
                                dp[i1][j1][i2][j2][i]=min(dp[i1][j1][i2][j2][i], min(dp[i1][j1][k][j2][i-1]+dp[k+1][j1][i2][j2][0], dp[i1][j1][k][j2][0]+dp[k+1][j1][i2][j2][i-1]));
                        }
        //套公式
        printf("%.3f
    ", sqrt(double(dp[1][1][8][8][n-1])/n-double(sum[8][8]*sum[8][8])/n/n));
        return 0;
    }
    
  • 相关阅读:
    07-selenium、PhantomJS(无头浏览器)
    06爬虫-异步协程
    Numpy数值类型与数值运算-03
    05爬虫-requests模块基础(2)
    初识Matplotlib-01
    03爬虫-requests模块基础(1)
    Django安装与简单事例-02
    JavaWeb学习总结(二):Http协议
    Tomcat学习总结(一):目录简介
    Web服务器学习总结(一):web服务器简介
  • 原文地址:https://www.cnblogs.com/LSlzf/p/10808638.html
Copyright © 2011-2022 走看看