[NOI1999] 棋盘分割

COGS 100. [NOI1999] 棋盘分割

http://www.cogs.pro/cogs/problem/problem.php?pid=100

★★   输入文件:division.in   输出文件:division.out   简单对比
时间限制:1 s   内存限制:128 MB

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

    原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成 n 块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 x i 为第 i 块矩形棋盘的分。
请编程对给出的棋盘及 n ,求出 的最小值。
 
【输入格式】
第 1 行为一个整数 n(1<n<9) 。<="" div="">

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

【输出格式】
   
仅一个数,为 (四舍五入精确到小数点后三位)。
【输入样例】
输入文件名:division.in

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
输出文件名:division.out
1.633
注意合法的切割方案是指 切了一刀后,选其中一块继续切,另一块不能再切
首先将均方差公式变形
 
第三个等号到第四个等号的变换:
 
平均值固定不变,所以只需最小化每个矩形总分的平方和
dp[k][x1][x2][y1][y2] 矩形左上角标号为(x1,y2) ,右上角标号为(x2,y2),分为k个矩形 所得到的最小平方和
初始化:dp 极大值
预处理:s[x1][x2][y1][y2]   矩形左上角标号为(x1,y2) ,右上角标号为(x2,y2)内所有数和的平方
          dp[1][x1][x2][y1][y2]=s[x1][x2][y1][y2] (相当于一刀也不切的情况)
状态转移:dp[k][x1][y1][x2][y2]=min(
             dp[k-1][x1][y1][x][y2]+s[x+1][y1][x2][y2]) //横着切,取上面一部分继续切
             dp[k-1][x+1][y1][x2][y2]+s[x1][y1][x][y2]//横着切,取下面一部分继续切
             dp[k-1][x1][y1][x2][y]+s[x1][y+1][x2][y2]//竖着切,取左边一部分继续切 
             dp[k-1][x1][y+1][x2][y2]+s[x1][y1][x2][y] //竖着切,取右边一部分继续切
)
转移过程可用记忆化搜索实现
 
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1e9
using namespace std;
int n,a[9][9],dp[16][9][9][9][9],s[9][9][9][9];
int add(int x1,int y1,int x2,int y2)
{
    int tmp=0;
    for(int i=x1;i<=x2;i++)
     for(int j=y1;j<=y2;j++)
      tmp+=a[i][j];
    return tmp;
}
int dfs(int k,int x1,int y1,int x2,int y2)
{
    if(dp[k][x1][y1][x2][y2]!=INF) return dp[k][x1][y1][x2][y2];
    if(x2>x1)
    {
        for(int i=x1;i<x2;i++)
        {
            int t1=dfs(k-1,x1,y1,i,y2),t2=dfs(k-1,i+1,y1,x2,y2);
            int t=min(t1+s[i+1][y1][x2][y2],t2+s[x1][y1][i][y2]);
            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
        }
    }
    if(y2>y1)
    {
        for(int i=y1;i<y2;i++)
        {
            int t1=dfs(k-1,x1,y1,x2,i),t2=dfs(k-1,x1,i+1,x2,y2);
            int t=min(t1+s[x1][i+1][x2][y2],t2+s[x1][y1][x2][i]);
            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
        }
    }
    return dp[k][x1][y1][x2][y2];
}
int main()
{
    /*freopen("division.in","r",stdin);
    freopen("division.out","w",stdout);*/
    scanf("%d",&n);
    for(int i=1;i<=8;i++)
     for(int j=1;j<=8;j++)
      scanf("%d",&a[i][j]);
    for(int k=1;k<=n;k++)
     for(int x1=1;x1<=8;x1++)
     for(int y1=1;y1<=8;y1++)
      for(int x2=1;x2<=8;x2++)
       for(int y2=1;y2<=8;y2++)
        dp[k][x1][y1][x2][y2]=INF;
    for(int x1=1;x1<=8;x1++)
     for(int y1=1;y1<=8;y1++)
      for(int x2=1;x2<=8;x2++)
       for(int y2=1;y2<=8;y2++)
        {
            int tmp=add(x1,x2,y1,y2);
            dp[1][x1][x2][y1][y2]=s[x1][x2][y1][y2]=tmp*tmp;
        }
    int tmp=dfs(n,1,1,8,8);
    int px=add(1,1,8,8);
    double t=((double) (px) / n )*((double) (px) / n );
    double r= (double) (tmp) / n;
    printf("%.3lf",sqrt(r-t));
}

 

posted @ 2017-03-03 09:40  TRTTG  阅读(400)  评论(0编辑  收藏  举报