[NOI1999] 棋盘分割
COGS 100. [NOI1999] 棋盘分割
http://www.cogs.pro/cogs/problem/problem.php?pid=100
★★ 输入文件:division.in
输出文件:division.out
简单对比
时间限制:1 s 内存限制:128 MB
【问题描述】
将一个8×8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将分割过的部分任选一块继续如此分割,这样割了n-1次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成 n 块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 x i 为第 i 块矩形棋盘的分。
请编程对给出的棋盘及 n ,求出 的最小值。
【输入格式】
第 1 行为一个整数 n(1<n<9) 。<="" div="">
第 2 行至第 9 行每行为 8 个小于 100 的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
【输出格式】
仅一个数,为 (四舍五入精确到小数点后三位)。
【输入样例】
输入文件名:division.in
3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3
输出文件名:division.out
1.633
注意合法的切割方案是指 切了一刀后,选其中一块继续切,另一块不能再切
首先将均方差公式变形
第三个等号到第四个等号的变换:
平均值固定不变,所以只需最小化每个矩形总分的平方和
dp[k][x1][x2][y1][y2] 矩形左上角标号为(x1,y2) ,右上角标号为(x2,y2),分为k个矩形 所得到的最小平方和
初始化:dp 极大值
预处理:s[x1][x2][y1][y2] 矩形左上角标号为(x1,y2) ,右上角标号为(x2,y2)内所有数和的平方
dp[1][x1][x2][y1][y2]=s[x1][x2][y1][y2] (相当于一刀也不切的情况)
状态转移:dp[k][x1][y1][x2][y2]=min(
dp[k-1][x1][y1][x][y2]+s[x+1][y1][x2][y2]) //横着切,取上面一部分继续切
dp[k-1][x+1][y1][x2][y2]+s[x1][y1][x][y2]//横着切,取下面一部分继续切
dp[k-1][x1][y1][x2][y]+s[x1][y+1][x2][y2]//竖着切,取左边一部分继续切
dp[k-1][x1][y+1][x2][y2]+s[x1][y1][x2][y] //竖着切,取右边一部分继续切
)
转移过程可用记忆化搜索实现
#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define INF 1e9 using namespace std; int n,a[9][9],dp[16][9][9][9][9],s[9][9][9][9]; int add(int x1,int y1,int x2,int y2) { int tmp=0; for(int i=x1;i<=x2;i++) for(int j=y1;j<=y2;j++) tmp+=a[i][j]; return tmp; } int dfs(int k,int x1,int y1,int x2,int y2) { if(dp[k][x1][y1][x2][y2]!=INF) return dp[k][x1][y1][x2][y2]; if(x2>x1) { for(int i=x1;i<x2;i++) { int t1=dfs(k-1,x1,y1,i,y2),t2=dfs(k-1,i+1,y1,x2,y2); int t=min(t1+s[i+1][y1][x2][y2],t2+s[x1][y1][i][y2]); dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t); } } if(y2>y1) { for(int i=y1;i<y2;i++) { int t1=dfs(k-1,x1,y1,x2,i),t2=dfs(k-1,x1,i+1,x2,y2); int t=min(t1+s[x1][i+1][x2][y2],t2+s[x1][y1][x2][i]); dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t); } } return dp[k][x1][y1][x2][y2]; } int main() { /*freopen("division.in","r",stdin); freopen("division.out","w",stdout);*/ scanf("%d",&n); for(int i=1;i<=8;i++) for(int j=1;j<=8;j++) scanf("%d",&a[i][j]); for(int k=1;k<=n;k++) for(int x1=1;x1<=8;x1++) for(int y1=1;y1<=8;y1++) for(int x2=1;x2<=8;x2++) for(int y2=1;y2<=8;y2++) dp[k][x1][y1][x2][y2]=INF; for(int x1=1;x1<=8;x1++) for(int y1=1;y1<=8;y1++) for(int x2=1;x2<=8;x2++) for(int y2=1;y2<=8;y2++) { int tmp=add(x1,x2,y1,y2); dp[1][x1][x2][y1][y2]=s[x1][x2][y1][y2]=tmp*tmp; } int tmp=dfs(n,1,1,8,8); int px=add(1,1,8,8); double t=((double) (px) / n )*((double) (px) / n ); double r= (double) (tmp) / n; printf("%.3lf",sqrt(r-t)); }