poj 1191 棋盘分割

题目意思,中文不解释:


——————————————

分割线

——————————————

根据题目的公式可得到,很多东东。

σ=sqrt(((x1-x)^2+(x2-x)^2+...+(xn-x)^2)/n),其中x为x1..xn的平均数.
所以σ^2=((x1-x)^2+(x2-x)^2+...+(xn-x)^2)/n
即:n*σ^2=(x1-x)^2+(x2-x)^2+...+(xn-x)^2
                =x1^2-2x*x1+x^2+x2^2-2x*x2+x^2+...+xn^2-2x*xn+x^2
                =x1^2+x2^2+...+xn^2-2x(x1+x2+...+xn)+n*x^2
                =x1^2+x2^2+...+xn^2-2x*n*x+n*x^2
                =x1^2+x2^2+...+xn^2-n*x^2
那么得到:

        n*σ^2+n*x^2=x1^2+x2^2+...+xn^2.


题目要求σ最小,而x为常数,其实质就是把棋盘分为每部分和分别为x1..xn的n部分,使得这n部分的平方和最小.既然要求被化简了,我们的任务也就很明确了.本题可以用一个记忆化搜索。用一个数组dp[N][x1][y1][x2][y2]来记录结果:

具体见代码:


#include<iostream>
#include<fstream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<sstream>
#include<cassert>
using namespace std;
#define LL __int64
#define min(a,b) a<b?a:b
#define inf 99999999
 
int a[15][15];
int n;
int temp;
int dp[16][15][19][19][19];
 
//根据数组a[][]存储的值求任意矩形的和
int getsum(int x1,int y1,int x2,int y2) {
    return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}
 
int getcat(int N,int x1,int y1,int x2,int y2) {
 
    if(dp[N][x1][y1][x2][y2]!=-1) return dp[N][x1][y1][x2][y2];
    //已经求过就可以直接返回了,避免重复运算导致超时(类似CF汉罗塔的一道题目,当时没这个优化超时)
    int s1,s2;
    //N==1说明切割了n-1次,最后剩下的一块矩形求出其和的平方直接返回
    if(N==1) {
        s1=getsum(x1,y1,x2,y2);
        dp[N][x1][y1][x2][y2]=s1*s1;
        return s1*s1;
    }
 
    int mi=inf;
    int temp1;
    //水平方向切
    for(int x=x1; x<x2; x++) {
        s1=getsum(x1,y1,x,y2);
        s2=getsum(x+1,y1,x2,y2);
        //横向将当前的矩形切割为两块后,继续将两块进行切割,最后返回时,选取得到的值较小的
        temp1=min(getcat(N-1,x1,y1,x,y2)+s2*s2,getcat(N-1,x+1,y1,x2,y2)+s1*s1);
        mi=min(mi,temp1);
 
    }
    //竖直方向切
    int temp2;
    for(int y=y1; y<y2; y++) {
        s1=getsum(x1,y1,x2,y);
        s2=getsum(x1,y+1,x2,y2);
        temp1=min(getcat(N-1,x1,y1,x2,y)+s2*s2,getcat(N-1,x1,y+1,x2,y2)+s1*s1);
        mi=min(mi,temp1);
 
    }
    dp[N][x1][y1][x2][y2]=mi;
    return mi;
    // mi记录横向和纵向切割后,值较小的
}
 
int main() {
    while(cin>>n) {
        int sum=0;
        memset(a,0,sizeof(a));
        memset(dp,-1,sizeof(dp));
        for(int i=1; i<=8; i++) {
            for(int j=1; j<=8; j++) {
                scanf("%d",&temp);
                a[i][j]=temp+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
                sum+=temp;
            }
        }
 
        double x=(sum*1.0)/n;
        int T=getcat(n,1,1,8,8);
        //根据公式可得到结果
        double ans=sqrt(abs((T*1.0)/n-x*x));
        printf("%.3f\n",ans);
    }
 
    return 0;
}
 
/*

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

*/
 
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值