[NOI1999] 棋盘分割 —— 二维区间DP

题面

题目
在这里插入图片描述
在这里插入图片描述

思路

f ( x 1 , y 1 , x 2 , y 2 , k ) f(x1,y1,x2,y2,k) f(x1,y1,x2,y2,k)

表示将子矩阵 ( x 1 , y 1 ) ( x 2 , y 2 ) (x1,y1)(x2,y2) (x1,y1)(x2,y2)切成k部分的所有方案数

我们从中取一个min


因为我们可以横着切 也可以竖着切;

我们先横着切;

切一刀以后分成上下两半;

我们可以选择其中的一边接着切,另一边可以直接用二维前缀和来计算;

竖着切同理;

注意这里一个二维坐标代表一个方格,比如下图是 8 ∗ 8 8*8 88的棋盘
在这里插入图片描述
则有下面的代码

	//注意这里每个点(二维坐标)代表一个格子,而不是一个单纯的点
    for(int i=x1;i<x2;++i){
        //拿上面一块继续切分
        ret = min(ret,dp(x1,y1,i,y2,k-1)+get_ret(i+1,y1,x2,y2));
        //拿下面一块继续切分
        ret = min(ret,dp(i+1,y1,x2,y2,k-1)+get_ret(x1,y1,i,y2));
    }
    for(int j=y1;j<y2;++j){
        //拿左边继续切
        ret = min(ret,dp(x1,y1,x2,j,k-1)+get_ret(x1,j+1,x2,y2));
        //拿右边继续切
        ret = min(ret,dp(x1,j+1,x2,y2,k-1)+get_ret(x1,y1,x2,j));
    }

这里是关于如何处理均方差

在这里插入图片描述

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 5 + 10;

int s[N][N];

double f[N][N][N][N][N];

int n;

double X;

int get(int x1,int y1,int x2,int y2){
    return s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1];
}
double get_ret(int x1,int y1,int x2,int y2){
    double ret = get(x1,y1,x2,y2) - X;
    return 1.0*ret*ret / n;
}
//k表示切成k份
double dp(int x1,int y1,int x2,int y2,int k){
    double &ret = f[x1][y1][x2][y2][k];
    if(ret >= 0) return ret;
    if(k == 1) return ret = get_ret(x1,y1,x2,y2);
    ret = 1e9;
    //注意这里每个点(二维坐标)代表一个格子,而不是一个单纯的点
    for(int i=x1;i<x2;++i){
        //拿上面一块继续切分
        ret = min(ret,dp(x1,y1,i,y2,k-1)+get_ret(i+1,y1,x2,y2));
        //拿下面一块继续切分
        ret = min(ret,dp(i+1,y1,x2,y2,k-1)+get_ret(x1,y1,i,y2));
    }
    for(int j=y1;j<y2;++j){
        //拿左边继续切
        ret = min(ret,dp(x1,y1,x2,j,k-1)+get_ret(x1,j+1,x2,y2));
        //拿右边继续切
        ret = min(ret,dp(x1,j+1,x2,y2,k-1)+get_ret(x1,y1,x2,j));
    }
    return ret;
}
void solve(){
    cin >> n;
    for(int i=1;i<=8;++i){
        for(int j=1;j<=8;++j){
            cin >> s[i][j];
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
        }
    }
    X = 1.0*s[8][8]/n;
    memset(f,0xc2,sizeof f);//double取较小值
    printf("%.3f\n",sqrt(dp(1,1,8,8,n)));
}

int main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值