codevs1711/洛谷P1436棋盘分割(noi1999)

这是一道神题…
虽然也可以算一道好题…

题目分析

注意,本篇涉及大量奇怪的数学推理,不适者谨入!
首先我们来分析方差。(x表示平均值)
s2=1n(xxi)2=1n(x2+x2i2xxi)=x2+1nx2i2xnxi
=x2+1nx2i2x2=1nx2ix2
而平均值是已知的嘛。棋盘的总权值除以分割的块数嘛。
所以我们只要求最小平方和嘛。
首先我们用a[x][y]表示左上角一直到(x,y)的权值前缀和。
那么我们可以预处理出每一个矩形d[x1][y1][x2][y2] (左上角:(x1,y1),右下角:(x2,y2) )的权值和了,根据容斥随便搞一搞可以得到:

d[x1][y1][x2][y2]=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];

然后我们就用f[i][x1][y1][x2][y2]表示以(x1,y1)为左上角,(x2,y2)为右下角的矩形切i刀的最小平方和,所以就有:

kl=min(f[0][x1][y1][j][y2]+f[i-1][j+1][y1][x2][y2],f[i-1][x1][y1][j][y2]+f[0][j+1][y1][x2][y2]);
kl=min(f[0][x1][y1][x2][j]+f[i-1][x1][j+1][x2][y2],f[i-1][x1][y1][x2][j]+f[0][x1][j+1][x2][y2]);
f[i][x1][y1][x2][y2]=kl;

分别表示横着切然后接着切下面那块,横着切然后接着切上面那块,竖着切然后接着切右边那块,竖着切然后接着切左边那块。

代码

#include<iostream>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int read(){
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
    return q;
}
int n;
int a[10][10],f[16][10][10][10][10];//a:矩阵前缀和
double ans,sum;
int main()
{
    int i,j,x1,y1,x2,y2,kl;
    n=read();
    for(i=1;i<=8;i++)
        for(j=1;j<=8;j++)
        a[i][j]=read(),a[i][j]+=a[i][j-1];
    for(i=2;i<=8;i++)
        for(j=1;j<=8;j++)a[i][j]+=a[i-1][j];
    for(x1=1;x1<=8;x1++)
    for(y1=1;y1<=8;y1++)
    for(x2=1;x2<=8;x2++)
    for(y2=1;y2<=8;y2++){
        kl=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
        f[0][x1][y1][x2][y2]=kl*kl;
    }
    for(i=1;i<n;i++)//i代表切几刀
    for(x1=1;x1<=8;x1++)
    for(y1=1;y1<=8;y1++)
    for(x2=1;x2<=8;x2++)
    for(y2=1;y2<=8;y2++){
        kl=999999999;
        for(j=x1;j<x2;j++){//横着切
            kl=min(kl,f[i-1][x1][y1][j][y2]+f[0][j+1][y1][x2][y2]);
            kl=min(kl,f[0][x1][y1][j][y2]+f[i-1][j+1][y1][x2][y2]);
        }
        for(j=y1;j<y2;j++){//竖着切
            kl=min(kl,f[i-1][x1][y1][x2][j]+f[0][x1][j+1][x2][y2]);
            kl=min(kl,f[0][x1][y1][x2][j]+f[i-1][x1][j+1][x2][y2]);
        }
        f[i][x1][y1][x2][y2]=kl;
    }
    sum=(a[8][8]*1.0/n);sum=sum*sum;//注意0.1要放在a[8][8]的后面乘
    sum=f[n-1][1][1][8][8]*1.0/n-sum;ans=sqrt(sum);
    printf("%.3lf",ans);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值