NOI 193棋盘分割.cpp

193:棋盘分割

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

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差,其中平均值,xi为第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
来源
Noi 99
 1 /*
 2 z
 3 */
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstring>
 7 #include<cmath>
 8 #define INF 0x3f3f3f3f
 9 using namespace std;
10 int n;
11 int s[9][9][9][9],c[9][9];
12 int f[20][9][9][9][9];
13 int add(int x1,int y1,int x2,int y2 )
14 {
15     int w=0;
16     for(int i=x1;i<=x2;i++)
17       for(int j=y1;j<=y2;j++)
18           w+=c[i][j];
19     return w;
20 }
21 int dfs(int k,int x1,int y1,int x2,int y2)
22 {
23     if(f[k][x1][y1][x2][y2]!=-1)
24        return f[k][x1][y1][x2][y2];
25     f[k][x1][y1][x2][y2]=INF;
26     if(x1<x2)//横切
27     {
28         for(int x=x1; x<x2; x++)
29         {
30             int t1=dfs(k-1,x+1,y1,x2,y2);  //取上面那么递归计算下面
31             int t2=dfs(k-1,x1,y1,x,y2);    //去下面那么递归计算上面
32             int t=min(t1+s[x1][y1][x][y2],t2+s[x+1][y1][x2][y2]);
33             f[k][x1][y1][x2][y2]=min(f[k][x1][y1][x2][y2],t);
34         }
35     }
36         if(y1<y2)
37         {
38             for(int y=y1;y<y2;y++)
39             {
40                 int t1=dfs(k-1,x1,y+1,x2,y2) ;   //选左边那么递归计算右边
41                 int t2=dfs(k-1,x1,y1,x2,y);     //选右边那么递归计算左边
42                 int t=min(t1+s[x1][y1][x2][y],t2+s[x1][y+1][x2][y2]);
43                 f[k][x1][y1][x2][y2]=min(f[k][x1][y1][x2][y2],t);
44             }
45         }
46     return f[k][x1][y1][x2][y2];
47 }
48 int main()
49 {
50     int x1,y1,x2,y2,n;
51     scanf("%d",&n);
52     memset(f,-1,sizeof(f));
53     for(int i=1;i<=8;i++)
54       for(int j=1;j<=8;j++)
55         scanf("%d",&c[i][j]);
56     for(x1=1; x1<=8; x1++)
57       for(x2=x1; x2<=8; x2++)
58         for(y1=1; y1<=8; y1++)
59           for(y2=y1; y2<=8; y2++)
60           {
61                int tmp=add(x1,y1,x2,y2);
62                f[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]=tmp*tmp;
63           }
64     dfs(n,1,1,8,8);
65     double X,ans;
66     X=1.*add(1,1,8,8);
67     X=(X/n)*(X/n);
68     //cout<<f[n][1][1][8][8]<<endl;
69     ans=sqrt(1.0*f[n][1][1][8][8]/n-X);
70     printf("%.3f\n",ans);
71     return 0;
72 }
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#define min(a,b) a<b?a:b
#define INF 0x3f3f3f3f
#define N 20
using namespace std;
int dp[N][10][10][10][10],s[10][10][10][10],c[10][10];
int add(int x1,int y1,int x2,int y2)
{
    int ans=0,x,y;
    for(x=x1; x<=x2; x++)
        for(y=y1; y<=y2; y++)
            ans+=c[x][y];
        coutMMans;
    return ans;
}
int dfs(int k,int x1,int y1,int x2,int y2)
{
    if(dp[k][x1][y1][x2][y2]!=-1)
        return dp[k][x1][y1][x2][y2];
    int x,y,t1,t2,t;
    dp[k][x1][y1][x2][y2]=INF;
    if(x2>x1)  //至少有两行才能横着切
    {
        //1.选上面:dp[k][x1][y1][x2][y2]=s[x1][y1][x][y2]+dp[k-1][x+1][y1][x2][y2];
        //2.选下面:dp[k][x1][y1][x2][y2]=s[x+1][y1][x2][y2]+dp[k-1][x1][y1][x][y2];
        for(x=x1; x<x2; x++)
        {
            t1=dfs(k-1,x+1,y1,x2,y2);  //取上面那么递归计算下面
            t2=dfs(k-1,x1,y1,x,y2);    //去下面那么递归计算上面
            t=min(t1+s[x1][y1][x][y2] , t2+s[x+1][y1][x2][y2]);
            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
        }
    }
    if(y2>y1) //至少有两列才能竖着切
    {
        //1.选左边:dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y]+dp[k-1][x1][y+1][x2][y2];
        //2.选右边:dp[k][x1][y1][x2][y2]=s[x1][y+1][x2][y2]+dp[k-1][x1][y1][x2][y];

        for(y=y1; y<y2; y++)
        {
            t1=dfs(k-1,x1,y+1,x2,y2); //选左边那么递归计算右边
            t2=dfs(k-1,x1,y1,x2,y);   //选右边那么递归计算左边
            t=min(t1+s[x1][y1][x2][y] , t2+s[x1][y+1][x2][y2]);
            dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t);
        }
    }

    return dp[k][x1][y1][x2][y2];
}
int main()
{
    int x1,x2,y1,y2,x,y,n;
    scanf("%d",&n);
    for(int i=1; i<=8; i++)
        for(int j=1; j<=8; j++)
            scanf("%d",&c[i][j]);
    memset(dp,-1,sizeof(dp));
    for(x1=1; x1<=8; x1++)
        for(x2=x1; x2<=8; x2++)
            for(y1=1; y1<=8; y1++)
                for(y2=y1; y2<=8; y2++)
                {
                    int tmp=add(x1,y1,x2,y2);
                    dp[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]=tmp*tmp;
                }
    dfs(n,1,1,8,8);
    double X,ans;
    X=1.*add(1,1,8,8);
    X=(X/n)*(X/n);
    cout<<X<<endl;
    ans=sqrt(1.0*dp[n][1][1][8][8]/n-X);
    printf("%.3f\n",ans);
    return 0;
}
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.矩形区域,采用Excel的方法
左上角(x1,y1):右下角(x2,y2)
2.状态:
f(i,x1,y1,x2,y2)表示
把(x1,y1):(x2,y2)分割成i块的最小segma((x-x)^2)。
 
对任意的矩形区域,i=1时的f值可以直接求得。
 
 
3.状态转移:f(i,x1,y1,x2,y2)
(1)枚举x1<=x<x2,横向分割成上下两个矩形
(x1,y1):(x,y2) (x+1,y1):(x2,y2)
把(x1,y1):(x,y2)分割成(i-1)矩形:f(i-1,x1,y1,x,y2)
(x+1,y1):(x2,y2)作为1个矩形: f(1,x+1,y1,x2,y2)
或者f(1,x1,y1,x,y2)+f(i-1,x+1,y1,x2,y2)
(2)同理,枚举y1<=y<y2,纵向分割成左右两个矩形
(x1,y1):(x2,y) (x1,y+1):(x2,y2)
f(i-1,x1,y1,x2,y)+f(1,x1,y+1,x2,y2)
f(1,x1,y1,x2,y)+f(i-1,x1,y+1,X2,Y2)
 
posted @ 2016-04-09 10:26  Minepressure  阅读(320)  评论(0编辑  收藏  举报