棋盘分割----POJ1191----DP

/*
=========================================
Author:Bob Lee
2012.8.24
=========================================
核心思想:动态规划
题目的大意是有一个8*8的矩阵,每一个都有一个非负整数
然后要你切n-1下
最后就有了n块,每块的和记为Xi
然后给出一个公式Q=sqrt( sum(Xi-XAve)^2/n );
要你求出Q的最小值
首先是把Q平方,这样可以方便运算。
得Q^2 = sum(Xi^2)/n - XAve^2
XAve就是平均值,这个好求
难点就在前面的那一个求和,这个我们得用DP求
这是LRJ的黑书上关于动态规划的一道题目,核心思想已经讲了,我就原样COPY
By LRJ:
dp[k][x1][y1][x2][y2]:左上角坐标为(x1,y1),右下角坐标为(x2,y2)
的棋盘,设它把切割k次以后得到的k+1块矩形的总分平方和最小值.
s[x1][y1][x2][y2]:左上角坐标为(x1,y1),右下角坐标为(x2,y2)
的棋盘的总和的平方
 dp[k][x1][y1][x2][y2] =
1)按横的划分: min(dp[k-1][x1][y1][f][y2]+s[f+1][y1][x2][y2]
    , dp[k-1][f+1][y1][x2][y2]+s[x1][y1][f][y2]);
2)按竖的划分: min(dp[k-1][x1][y1][x2][f]+s[x1][f+1][x2][y2]
    , dp[k-1][x1][f+1][x2][y2]+s[x1][y1][x2][f]);
我把dp 换成了d
这里还有一个地方就是处理s这个数组,这个还真的有点费脑筋(本人菜鸟)
我就是用的一个count-map数组保存的由1,1到x,y的和,这样就可一通过
double ans = count_map[i2][j2] - count_map[i2][j-1] - count_map[i-1][j2] + count_map[i-1][j-1];
这个公式搞定来,自己可以演算一下
还有一个精度问题,是用long double才AC的,用double就过不了,表示很纠结
*/




#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

int n;
long double d[16][9][9][9][9];
long double s[9][9][9][9];
double map[9][9];
int count_map[9][9];
long double sum;
long double ave;
const int m = 8;
const double INF = 9999999999;

double count(int i,int j,int i2,int j2)
{
    double ans = count_map[i2][j2] - count_map[i2][j-1] - count_map[i-1][j2] + count_map[i-1][j-1];
    return ans*ans;
}

void init()  //初始化s这个数组
{
    int i,j;
    sum = 0;
    memset(count_map,0,sizeof(count_map));
    for(i=1;i<=8;i++)
    {
        for(j=1;j<=8;j++)
        {
            scanf("%lf",&map[i][j]);
            sum+=map[i][j];

            //count_map[i][j] 表示的是1,1到i,j的矩阵范围内的和
            count_map[i][j] =   count_map[i][j-1]   +
                                count_map[i-1][j]   -
                                count_map[i-1][j-1] +
                                map[i][j];
        }
    }

    ave = sum*1.0/n;
    int i2,j2;

    for(i=1;i<=m;i++)
    {
        for(j=1;j<=m;j++)
        {
            for(i2=i;i2<=m;i2++)
            {
                for(j2 = j;j2<=m;j2++)
                {
                    s[i][j][i2][j2] = count(i,j,i2,j2);
                    d[0][i][j][i2][j2] = s[i][j][i2][j2];
                }
            }
        }
    }
}

void dp()
{
    int k,i,j,i2,j2;

    for(k=1;k<n;k++)
    {
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=m;j++)
            {
                for(i2=i;i2<=m;i2++)
                {
                    for(j2 = j;j2<=m;j2++)
                    {
                        d[k][i][j][i2][j2] = INF;

                        //首先是横着切
                        int t;
                        for( t = i;t<i2;t++)
                            d[k][i][j][i2][j2] =    min(d[k][i][j][i2][j2],
                                                        min(d[k-1][i][j][t][j2] + s[t+1][j][i2][j2],
                                                            s[i][j][t][j2] + d[k-1][t+1][j][i2][j2]));
                        //在就是竖着切
                        for( t = j;t<j2;t++)
                            d[k][i][j][i2][j2] =    min(d[k][i][j][i2][j2],
                                                        min(d[k-1][i][j][i2][t] + s[i][t+1][i2][j2],
                                                            s[i][j][i2][t] + d[k-1][i][t+1][i2][j2]));
                    }
                }
            }
        }
    }
}



int main()
{
    while(scanf("%d",&n) != EOF)
    {
        init();
        dp();
        long double ans = d[n-1][1][1][m][m]*1.0/n - ave*ave;
        printf("%.3f\n",(double)sqrt(ans));
    }
    return 0;
}


题目地址:http://poj.org/problem?id=1191



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值