您的位置:首页 > 其它

POJ 1191棋盘分割

2009-10-08 15:19 330 查看
棋盘分割

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 5827Accepted: 2092
>
>

Description
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)



原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差

,其中平均值

,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
Input
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
Output
仅一个数,为O'(四舍五入精确到小数点后三位)。
Sample Input
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

Sample Output
1.633

Source
Noi 99

/*http://acm.pku.edu.cn/JudgeOnline/problem?id=1191*/
#include <iostream>
#include <cmath>
#define minv(a, b) ((a) <= (b) ? (a) : (b))
#define MAX_N 8
using namespace std;
int input[MAX_N + 1][MAX_N + 1];
//pointV[i][j][k][l]代表左上方点位(i, j),右下方点为(k,l)的矩形的权值和,这个矩阵通过
//getVal预先算好
int pointV[MAX_N + 1][MAX_N + 1][MAX_N + 1][MAX_N + 1];
/*
minVals[i][j][k][l]
代表(i,j)(k,l)构成的矩阵划分为n块可以得到的最小平分差和
minVals[i][j][k][l][1]一开始先算好,和poinV一块算
则状态转移方程为(r表示切割处):
1)
横切,取下面的块继续切
minVals(i, j, k, l, n) = minv(minVals(i, j, k, l, n), minVals(i, j, r - 1, l, 1) + minVals(r, j, k, l, n - 1))
横切,取上面的块继续切
minVals(i, j, k, l, n) = minv(minVals(i, j, k, l, n), minVals(r, j, k, l, 1) + minVals(i, j, r - 1, l, n - 1))
2)
树切,取左面的块继续切
minVals(i, j, k, l, n) = minv(minVals(i, j, k, l, n), minVals(i, c, k, l, 1) + minVals(i, j, k, c - 1, n - 1))
树切,取右面的块继续切
minVals(i, j, k, l, n) = minv(minVals(i, j, k, l, n), minVals(i, j, k, c - 1, 1) + minVals(i, c, k, l, n - 1))
最后结果是sqrt(minVals(i, j, k, l, n) / n)
另外需要注意的时候需要判断是否可以继续切
n*m的矩形最多可以切成n * m - 1块
*/
double minVals[MAX_N + 1][MAX_N + 1][MAX_N + 1][MAX_N + 1][16];
double average;
int n;
void getVal()
{
int x1, y1, x2, y2;
for(x1 = 1; x1 <= MAX_N; x1++)
{
for(y1 = 1; y1 <= MAX_N; y1++)
{
for(x2 = x1; x2 <= MAX_N; x2++)
{
for(y2 = y1; y2 <= MAX_N; y2++)
{
if(!(x1 == x2 && y1 == y2))
{
pointV[x1][y1][x2][y2] = pointV[x1][y1][x2 - 1][y2]
+ pointV[x1][y1][x2][y2 - 1] - pointV[x1][y1][x2 - 1][y2 - 1]
+ pointV[x2][y2][x2][y2];
}
minVals[x1][y1][x2][y2][1] = pow(pointV[x1][y1][x2][y2] - average, double(2));
for(int k1 = 2; k1 <= 15; k1++)
minVals[x1][y1][x2][y2][k1] = INT_MAX;
}
}
}
}
}
double dp(int x1, int y1, int x2, int y2, int n)
{
if(minVals[x1][y1][x2][y2]
!= INT_MAX)
return minVals[x1][y1][x2][y2]
;
double curVal = INT_MAX;
//横切
for(int r = x1 + 1; r <= x2; r++)
{
//判断切完后剩下的块是否可以继续切
if(x2 - r + 1 + y2 - y1 >= n - 1)
curVal = minv(curVal, minVals[x1][y1][r - 1][y2][1] + dp(r, y1, x2, y2, n - 1));
//判断切完后剩下的块是否可以继续切
if(r - x1 + y2 - y1 >= n - 1)
curVal = minv(curVal, minVals[r][y1][x2][y2][1] + dp(x1, y1, r - 1, y2, n - 1));
}
//竖切
for(int c = y1 + 1; c <= y2; c++)
{
//判断切完后剩下的块是否可以继续切
if(x2 - x1 + 1 + y2 - c >= n - 1)
curVal = minv(curVal, minVals[x1][y1][x2][c - 1][1] + dp(x1, c, x2, y2, n - 1));
//判断切完后剩下的块是否可以继续切
if(x2 - x1 + c - y1 >= n - 1)
curVal = minv(curVal, minVals[x1][c][x2][y2][1] + dp(x1, y1, x2, c - 1, n - 1));
}
minVals[x1][y1][x2][y2]
= curVal;
return curVal;
}
int main()
{
scanf("%d", &n);
int i, j;
average = 0;
for(i = 1; i <= MAX_N; i++)
for(j = 1; j <= MAX_N; j++)
{
scanf("%d", &input[i][j]);
pointV[i][j][i][j] = input[i][j];
average += input[i][j];
}
average = average / n;
getVal();
dp(1, 1, 8, 8, n);
printf("%.3f/n", sqrt(minVals[1][1][8][8]
/ n));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: