[NOI1999][openjudge]棋盘分割(数学相关+dp)

这篇博客详细介绍了NOI1999年竞赛中的一道棋盘分割问题,重点在于理解如何通过动态规划(dp)方法找到最小均方差的解。博主强调了正确读题的重要性,并分享了代码实现,提醒注意矩阵前缀和计算时的细节处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

传送门

题解

NOI1999的dp都好厉害呀= =
一定要认真读题!
先看这个均方差的式子,可以进行化简:
δ=ni=1(xix¯)2n
δ2=1nni=1(xix¯)2
=1nni=1(x2i2xix¯+x¯2)
=1nni=1x2i1nni=12xix¯+1nni=1x¯2
=1nni=1x2i2x¯1nni=1xi+1nni=1x¯2
=1nni=1x2ix¯2
显然 1n x¯2 都是定值,那么我们的任务是求 ni=1x2i 的最小值。
设f(i,a,b,c,d)表示切第i刀,剩余的矩形左上角和右下角的坐标是(a,b)和(c,d),除了剩余部分其它部分的xi平方和的最小值。那么f(i)可以向f(i+1)转移,只需要暴力枚举第i+1刀从哪里切了一刀即可。
矩阵前缀和搞的时候细节比较多,一定要细心。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 20

int n;
double x,y,Min,ans;
double v[N][N],s[N][N],f[N][10][10][10][10];

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=8;++i)
        for (int j=1;j<=8;++j) scanf("%lf",&v[i][j]);
    for (int i=1;i<=8;++i)
        for (int j=1;j<=8;++j)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+v[i][j];        

    memset(f,127,sizeof(f));Min=f[0][0][0][0][0];
    for (int a=1;a<8;++a)
    {
        x=s[8][8]-s[8][a];
        f[1][1][1][8][a]=x*x;
        x=s[8][a];
        f[1][1][a+1][8][8]=x*x;

        x=s[a][8];
        f[1][a+1][1][8][8]=x*x;
        x=s[8][8]-s[a][8];
        f[1][1][1][a][8]=x*x;
    }
    for (int i=1;i<n-1;++i)
        for (int a=1;a<=8;++a)
            for (int b=1;b<=8;++b)
                for (int c=a;c<=8;++c)
                    for (int d=b;d<=8;++d)
                    {
                        x=f[i][a][b][c][d];
                        for (int e=a;e<c;++e)
                        {
                            y=s[c][d]-s[c][b-1]-s[e][d]+s[e][b-1];
                            f[i+1][a][b][e][d]=min(f[i+1][a][b][e][d],x+y*y);
                            y=s[e][d]-s[e][b-1]-s[a-1][d]+s[a-1][b-1];
                            f[i+1][e+1][b][c][d]=min(f[i+1][e+1][b][c][d],x+y*y);
                        }
                        for (int e=b;e<d;++e)
                        {
                            y=s[c][d]-s[c][e]-s[a-1][d]+s[a-1][e];
                            f[i+1][a][b][c][e]=min(f[i+1][a][b][c][e],x+y*y);
                            y=s[c][e]-s[c][b-1]-s[a-1][e]+s[a-1][b-1];
                            f[i+1][a][e+1][c][d]=min(f[i+1][a][e+1][c][d],x+y*y);
                        }
                    }
    for (int a=1;a<=8;++a)
        for (int b=1;b<=8;++b)
            for (int c=a;c<=8;++c)
                for (int d=b;d<=8;++d)
                {
                    x=f[n-1][a][b][c][d];
                    y=s[c][d]-s[c][b-1]-s[a-1][d]+s[a-1][b-1];
                    Min=min(Min,x+y*y);
                }
    double t=Min/(n+0.0);
    ans=Min/(n+0.0)-(s[8][8]/(n+0.0))*(s[8][8]/(n+0.0));
    ans=sqrt(ans);
    printf("%0.3lf\n",ans);
}

总结

①认真读题!认真读题!认真读题!
②抵制手残!抵制手残!抵制手残!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值