NOI 193棋盘分割.cpp
193:棋盘分割
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
- 将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差,其中平均值,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。 - 输入
- 第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。 - 输出
- 仅一个数,为O'(四舍五入精确到小数点后三位)。
- 样例输入
-
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.633
- 来源
- Noi 99
1 /* 2 z 3 */ 4 #include<iostream> 5 #include<cstdio> 6 #include<cstring> 7 #include<cmath> 8 #define INF 0x3f3f3f3f 9 using namespace std; 10 int n; 11 int s[9][9][9][9],c[9][9]; 12 int f[20][9][9][9][9]; 13 int add(int x1,int y1,int x2,int y2 ) 14 { 15 int w=0; 16 for(int i=x1;i<=x2;i++) 17 for(int j=y1;j<=y2;j++) 18 w+=c[i][j]; 19 return w; 20 } 21 int dfs(int k,int x1,int y1,int x2,int y2) 22 { 23 if(f[k][x1][y1][x2][y2]!=-1) 24 return f[k][x1][y1][x2][y2]; 25 f[k][x1][y1][x2][y2]=INF; 26 if(x1<x2)//横切 27 { 28 for(int x=x1; x<x2; x++) 29 { 30 int t1=dfs(k-1,x+1,y1,x2,y2); //取上面那么递归计算下面 31 int t2=dfs(k-1,x1,y1,x,y2); //去下面那么递归计算上面 32 int t=min(t1+s[x1][y1][x][y2],t2+s[x+1][y1][x2][y2]); 33 f[k][x1][y1][x2][y2]=min(f[k][x1][y1][x2][y2],t); 34 } 35 } 36 if(y1<y2) 37 { 38 for(int y=y1;y<y2;y++) 39 { 40 int t1=dfs(k-1,x1,y+1,x2,y2) ; //选左边那么递归计算右边 41 int t2=dfs(k-1,x1,y1,x2,y); //选右边那么递归计算左边 42 int t=min(t1+s[x1][y1][x2][y],t2+s[x1][y+1][x2][y2]); 43 f[k][x1][y1][x2][y2]=min(f[k][x1][y1][x2][y2],t); 44 } 45 } 46 return f[k][x1][y1][x2][y2]; 47 } 48 int main() 49 { 50 int x1,y1,x2,y2,n; 51 scanf("%d",&n); 52 memset(f,-1,sizeof(f)); 53 for(int i=1;i<=8;i++) 54 for(int j=1;j<=8;j++) 55 scanf("%d",&c[i][j]); 56 for(x1=1; x1<=8; x1++) 57 for(x2=x1; x2<=8; x2++) 58 for(y1=1; y1<=8; y1++) 59 for(y2=y1; y2<=8; y2++) 60 { 61 int tmp=add(x1,y1,x2,y2); 62 f[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]=tmp*tmp; 63 } 64 dfs(n,1,1,8,8); 65 double X,ans; 66 X=1.*add(1,1,8,8); 67 X=(X/n)*(X/n); 68 //cout<<f[n][1][1][8][8]<<endl; 69 ans=sqrt(1.0*f[n][1][1][8][8]/n-X); 70 printf("%.3f\n",ans); 71 return 0; 72 }
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> #define min(a,b) a<b?a:b #define INF 0x3f3f3f3f #define N 20 using namespace std; int dp[N][10][10][10][10],s[10][10][10][10],c[10][10]; int add(int x1,int y1,int x2,int y2) { int ans=0,x,y; for(x=x1; x<=x2; x++) for(y=y1; y<=y2; y++) ans+=c[x][y]; coutMMans; return ans; } int dfs(int k,int x1,int y1,int x2,int y2) { if(dp[k][x1][y1][x2][y2]!=-1) return dp[k][x1][y1][x2][y2]; int x,y,t1,t2,t; dp[k][x1][y1][x2][y2]=INF; if(x2>x1) //至少有两行才能横着切 { //1.选上面:dp[k][x1][y1][x2][y2]=s[x1][y1][x][y2]+dp[k-1][x+1][y1][x2][y2]; //2.选下面:dp[k][x1][y1][x2][y2]=s[x+1][y1][x2][y2]+dp[k-1][x1][y1][x][y2]; for(x=x1; x<x2; x++) { t1=dfs(k-1,x+1,y1,x2,y2); //取上面那么递归计算下面 t2=dfs(k-1,x1,y1,x,y2); //去下面那么递归计算上面 t=min(t1+s[x1][y1][x][y2] , t2+s[x+1][y1][x2][y2]); dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t); } } if(y2>y1) //至少有两列才能竖着切 { //1.选左边:dp[k][x1][y1][x2][y2]=s[x1][y1][x2][y]+dp[k-1][x1][y+1][x2][y2]; //2.选右边:dp[k][x1][y1][x2][y2]=s[x1][y+1][x2][y2]+dp[k-1][x1][y1][x2][y]; for(y=y1; y<y2; y++) { t1=dfs(k-1,x1,y+1,x2,y2); //选左边那么递归计算右边 t2=dfs(k-1,x1,y1,x2,y); //选右边那么递归计算左边 t=min(t1+s[x1][y1][x2][y] , t2+s[x1][y+1][x2][y2]); dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],t); } } return dp[k][x1][y1][x2][y2]; } int main() { int x1,x2,y1,y2,x,y,n; scanf("%d",&n); for(int i=1; i<=8; i++) for(int j=1; j<=8; j++) scanf("%d",&c[i][j]); memset(dp,-1,sizeof(dp)); for(x1=1; x1<=8; x1++) for(x2=x1; x2<=8; x2++) for(y1=1; y1<=8; y1++) for(y2=y1; y2<=8; y2++) { int tmp=add(x1,y1,x2,y2); dp[1][x1][y1][x2][y2]=s[x1][y1][x2][y2]=tmp*tmp; } dfs(n,1,1,8,8); double X,ans; X=1.*add(1,1,8,8); X=(X/n)*(X/n); cout<<X<<endl; ans=sqrt(1.0*dp[n][1][1][8][8]/n-X); printf("%.3f\n",ans); return 0; }
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.矩形区域,采用Excel的方法
左上角(x1,y1):右下角(x2,y2)
2.状态:
f(i,x1,y1,x2,y2)表示
把(x1,y1):(x2,y2)分割成i块的最小segma((x-x)^2)。
对任意的矩形区域,i=1时的f值可以直接求得。
3.状态转移:f(i,x1,y1,x2,y2)
(1)枚举x1<=x<x2,横向分割成上下两个矩形
(x1,y1):(x,y2) (x+1,y1):(x2,y2)
把(x1,y1):(x,y2)分割成(i-1)矩形:f(i-1,x1,y1,x,y2)
(x+1,y1):(x2,y2)作为1个矩形: f(1,x+1,y1,x2,y2)
或者f(1,x1,y1,x,y2)+f(i-1,x+1,y1,x2,y2)
(2)同理,枚举y1<=y<y2,纵向分割成左右两个矩形
(x1,y1):(x2,y) (x1,y+1):(x2,y2)
f(i-1,x1,y1,x2,y)+f(1,x1,y+1,x2,y2)
f(1,x1,y1,x2,y)+f(i-1,x1,y+1,X2,Y2)