题目
http://noi.openjudge.cn/ch0405/193/
描述
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差,其中平均值,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O’的最小值。
输入
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出
仅一个数,为O’(四舍五入精确到小数点后三位)。
思路
区间 dp, dp[k][x1][y1][x2][y2] 表示将,(x1,y1):(x2,y2)区间内的矩形分成 k 块,均方误差的最小值
该问题是一个一对多问题,即两个子矩形的最优解拼接为一个大矩形后不一定为最优解,但是一个大矩形的最优解一定可以由两个子矩形的解拼接而成,所以针对子矩形遍历其横向划分和纵向划分。
其中要最小化平方和即可,具体推导过程参考该博客
https://blog.csdn.net/nnnnnnnnnnnny/article/details/51592665
源码
//
// 193.cpp
// test
//
// Created by sgxm on 2020/8/3.
// Copyright © 2020 sgxm. All rights reserved.
//
#include <stdio.h>
#include <iostream>
#include <math.h>
#include <memory.h>
#include <iomanip>
#include <string.h>
#define maxn 15
using namespace std;
int dp[15][8][8][8][8];
int s[8][8][8][8];
int tb[8][8];
int n;
int calSum(int x1,int y1,int x2,int y2){
int sum = 0;
for(int i = x1;i<=x2;++i){
for(int j = y1;j <= y2;++j){
sum+=tb[i][j];
}
}
return sum*sum;
}
int calDp(int k,int x1,int y1,int x2,int y2){
int MIN = 99999999;
if(dp[k][x1][y1][x2][y2]!=-1){
return dp[k][x1][y1][x2][y2];
}
if(k == 1){
return dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y2];
}
dp[k][x1][y1][x2][y2]=MIN;
//横向划分
for(int x = x1;x<x2;++x){
int t1 = calDp(k-1, x1, y1, x, y2);
int t2 = calDp(k-1,x+1,y1,x2,y2);
int t = min(t1+s[x+1][y1][x2][y2],t2+s[x1][y1][x][y2]);
MIN = min(MIN,t);
}
for(int y = y1;y<y2;++y){
int t1 = calDp(k-1,x1,y1,x2,y);
int t2 = calDp(k-1,x1,y+1, x2, y2);
int t = min(t1+s[x1][y+1][x2][y2],t2+s[x1][y1][x2][y]);
MIN = min(MIN,t);
}
MIN=min(MIN,dp[k][x1][y1][x2][y2]);
//cout<<k<< " "<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<" = "<<MIN<<endl;
return dp[k][x1][y1][x2][y2]=MIN;
}
int main(){
cin >> n;
for(int i = 0;i < 8;++i){
for(int j = 0;j < 8;++j){
cin >> tb[i][j];
}
}
memset(dp, -1,sizeof(dp));
//init Sum
for(int x1 = 0;x1 < 8;++x1){
for(int y1 = 0;y1 < 8;++y1){
for(int x2 = x1;x2<8;++x2){
for(int y2 = y1;y2<8;++y2){
s[x1][y1][x2][y2]=calSum(x1,y1,x2,y2);
}
}
}
}
//calDp(2,5,5,6,7);
int sum2 = calDp(n, 0, 0, 7, 7);
double res = 1.0*n*calDp(n, 0, 0, 7, 7)-s[0][0][7][7];
cout<<setiosflags(ios::fixed)<<setprecision(3)<<sqrt(res/(n*n))<<endl;
return 0;
}