题面
思路
用 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
8∗8的棋盘
则有下面的代码
//注意这里每个点(二维坐标)代表一个格子,而不是一个单纯的点
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;
}