poj 1191 棋盘分割

一道简单易错的动态规划问题

棋盘分割
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 13687 Accepted: 4873

Description

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

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

Input

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

Output

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

Sample Input

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

Sample Output

1.633

Source


详见注释


/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source£º
 *   @type:
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<climits>
#include<cmath>
#include<algorithm>
#define sqr(x)  ((x)*(x))
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 14    ;
//const int maxV=12    ;
int n;
int a[11][11];//代表第x行,第y列上的数大小
/*
原点坐标为[0][0],[x1][x2][y1][y2]通过了4个坐标描述了一个矩形,其中x轴竖直向下,y轴水平向右
*/
int pre[11][11];//pre[x][y]代表矩形[0][x][0][y]的数和
int dp[maxn+2][10][10][10][10];//[切的次数][x1][x2][y1][y2]的最小差的平方和


int cal(int x1,int x2,int y1,int y2)
{
    int tmp= pre[x2][y1]+pre[x1][y2]-pre[x1][y1];
    return sqr( pre[x2][y2]-tmp );
}
int DP(int cnt,int x1,int x2,int y1,int y2)
{
    if(dp[cnt][x1][x2][y1][y2]!=-1)  return dp[cnt][x1][x2][y1][y2];

//    if(x2-x1-1+y2-y1-1<cnt)  return dp[cnt][x1][x2][y1][y2]=INF;

    if(cnt==1)  return dp[cnt][x1][x2][y1][y2]=cal(x1,x2,y1,y2);


    int &ans=dp[cnt][x1][x2][y1][y2];
    ans=INF;
    /*INF是一个上限,返回值一定不超过INF,所以INF的选取很讲究,对于返回式int的问题,
    最好不要超过INT_MAX/2,因为常见的dp问题都是两个数相加,INF选取不当很容易爆int
    */
    for(int k=x1+1;k<x2;k++)   
    {
        if(x2-k-1+y2-y1-1>=cnt-2)/*加上判断条件可以省一点时间,其实也可以不加,也是对的,因为
        如果某块大小不够切一定的次数,返回值一定是INF
            */
        ans=min(ans, cal(x1,k,y1,y2 )+DP(cnt-1,k,x2,y1,y2)  );

        if(k-x1-1+y2-y1-1>=cnt-2)
        ans=min(ans, DP(cnt-1,x1,k,y1,y2 )+cal(k,x2,y1,y2)  );
    }
    for(int k=y1+1;k<y2;k++)
    {
        if(x2-x1-1+y2-k-1>=cnt-2)
        ans=min(ans, cal(x1,x2,y1,k) +DP(cnt-1,x1,x2 ,k,y2)  );

        if(x2-x1-1+k-y1-1>=cnt-2)
        ans=min(ans, DP(cnt-1,x1,x2,y1,k) +cal(x1,x2 ,k,y2)  );
    }
    return ans;

}
void precope()
{

    memset(pre[0],0,sizeof pre[0]);
    for(int x=1;x<=8;x++)
    {
        pre[x][0]=0;
        int sum=0;
        for(int y=1;y<=8;y++)
        {
            sum+=a[x][y];
            pre[x][y]=pre[x-1][y]+sum;
        }
    }

}
int main()
{
/*

这个题目的答案是sqrt(   (  sum(xi^2)-2*ave(x)*sum(xi)+sum(ave(x)^2) )/n     );

动态规划问题求得是最小的 sum(xi^2)

我想说说为什么求的不是:
最小的 sum(  (xi-ave(x))^2 )

如果求得是这个,不是很直接,很自然吗?

很简单因为ave(x)是double型变量,dp返回的是int,当然不能这么弄

之前一直wa,看了很多题解,他们都说黑书上dp的意义是  最小的 sum(xi^2),

后来我反复看代码,发现ave(x) 是double变量 是wa的根本原因。


这个scanf("%d",&n) 加不加!EOF 都可以AC,

题目里面并没有明确说有多组数据。(肯定有的)

*/
        scanf("%d",&n);
        double sum=0;
        for(int x=1;x<=8;x++)
        {
            for(int y=1;y<=8;y++)
            {
                scanf("%d",&a[x][y]);
                sum+=a[x][y];
            }
        }
        double ave=sum/n;
        precope();
        memset(dp,-1,sizeof dp);
        double tmp=DP(n,0,8,0,8);
        tmp=tmp-2*ave*sum+ n*sqr(ave);
        tmp/=n;
        tmp=sqrt(tmp);
        printf("%.3f\n",tmp);


/*
注释写了这么多,都是做题时思考到的,很容易错的地方,这个题告诉我们细节很重要:

变量的类型导致的精度问题,变量的范围导致的爆int问题...etc,

有时还会少些一个~导致TLE的问题,无穷大INF设置是否合理...etc...

变量是否有初始化,边界条件是否考虑到,有没有进行特判?

精度问题有没有考虑到?有时侯输入的%d写成了%lld直接成了wa。

有时侯wa往往离ac只有一步之遥,却总在关键时刻想不到。

%lld?%I64d 

是取最大还是最小想清楚没?

这上下两行代码换了个位置就对了,你想清区别没有?



*/

   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值