Description
將一個8*8的棋盤進行如下分割:將原棋盤割下一塊矩形棋盤並使剩下部分也是矩形,再將剩下的部分繼續如此分割,這樣割了(n-1)次後,連同最後剩下的矩形棋盤共有n塊矩形棋盤。(每次切割都只能沿著棋盤格子的邊進行)
原棋盤上每一格有一個分值,一塊矩形棋盤的總分為其所含各格分值之和。現在需要把棋盤按上述規則分割成n塊矩形棋盤,並使各矩形棋盤總分的均方差最小。
均方差 σ=∑ni=1(xi−x¯)2n−−−−−−−−−√ σ=\sqrt{\frac{\sum^n_{i=1}{(x_i-\overline{x})^2}}{n}} ,其中平均值 x¯=∑ni=1xin \overline{x}=\frac{\sum^n_{i=1}{x_i}}{n} ,xi為第i塊矩形棋盤的總分。
請編程對給出的棋盤及n,求出O’的最小值。
Input
第1行為一個整數n(1 < n < 15)。
第2行至第9行每行為8個小於100的非負整數,表示棋盤上相應格子的分值。每行相鄰兩數之間用一個空格分隔。
Output
僅一個數,為O’(四捨五入精確到小數點後三位)。
Sample Input
31 1 1 1 1 1 1 31 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 11 1 1 1 1 1 1 01 1 1 1 1 1 0 3
Sample Output
1.633
思路
首先對方差公式進行化簡,化簡之後為 σ2=∑ni=1x2in−x¯2 σ^2=\frac{\sum^n_{i=1}{x_i^2}}{n}-\overline{x}^2
然後使用dfs搜尋時進行切割, dp[x][y][a][b][k] 代表進行 n-k 次切割以後矩陣 (x,y)-(a,b) 分值和的平方。
因為平均數是定值,要使方差最小,只能讓各塊的和儘可能的小。
AC 代碼
#include<iostream>#include<algorithm>#include<stdio.h>#include<string.h>#include<math.h>#include<iostream>using namespace std;#include<queue>#include<map>#define INF (1<<25)int n,sum[10][10];int dp[10][10][10][10][16];int get(int x,int y,int a,int b){ return sum[a][b]-sum[a][y-1]-sum[x-1][b]+sum[x-1][y-1];}int dfs(int x,int y,int a,int b,int k){ if(dp[x][y][a][b][k]!=-1)return dp[x][y][a][b][k]; //記憶化搜尋 if(k==1)return (dp[x][y][a][b][k]=get(x,y,a,b)*get(x,y,a,b)); //返回當前塊的和的平方 int minn=INF; for(int i=x; i<a; i++) //水平切割 { int l=get(x,y,i,b); int r=get(i+1,y,a,b); minn=min(minn,min(dfs(x,y,i,b,k-1)+r*r,dfs(i+1,y,a,b,k-1)+l*l)); } for(int i=y; i<b; i++) //垂直切割 { int l=get(x,y,a,i); int r=get(x,i+1,a,b); minn=min(minn,min(dfs(x,y,a,i,k-1)+r*r,dfs(x,i+1,a,b,k-1)+l*l)); } return (dp[x][y][a][b][k]=minn);}int main(){ while(~scanf("%d",&n)) { int temp; memset(sum,0,sizeof(sum)); memset(dp,-1,sizeof(dp)); for(int i=1; i<=8; i++) for(int j=1; j<=8; j++) { scanf("%d",&temp); sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+temp; //從(1,1)到(i,j)地區和 } double avi=sum[8][8]*1.0/n; printf("%.3f\n",sqrt(dfs(1,1,8,8,n)*1.0/n-avi*avi)); } return 0;}