备战最后一次问求,这两周要多做些题目了。今晚做了一道poj的题目,题面要求对8*8的棋盘进行分割,使得分割的各块之间的均方差最小。由于棋盘可能要用到多处对不同块的求和,对棋盘进行预处理算出从(i,j)到(k,l)的块的和是必要的(虽然感觉不做这一步应该也不会超时吧,但我这么优化完0ms就过了)。同时,分割不同块的时候,明显有一个规模更小的子问题的求解过程,可以考虑采用动态规划。同时由于题面要求,不能同时对分割的两块进行再分割,需要注意递归式的写法;另外数组可能要开很大,但是暂时没想到好办法避免。开成9*9*9*9*16的至少在我的实现中较为好理解吧。
// chessboardParting.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAXN 16
int n;
int chessBoard[8][8];
int sum[9][9];
double dp[9][9][9][9][MAXN];//dp(u,v,x,y,k) means the maximum gotten from cutting from (u,v) to (x,y) for k times
double average;
double powe2(double a) { return a * a; }
double blockSum(int x1, int y1, int x2, int y2)
{
return powe2(sum[x1][y1] - sum[x1][y2] - sum[x2][y1] + sum[x2][y2] - average);
}
void init()
{
average = 0;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
scanf("%d", &chessBoard[i][j]);
average += chessBoard[i][j];
}
average /= n;
}
double minSeparation(int u, int v, int x, int y, int times)
{
assert(u < x&&v < y&× >= 2);
double ret = 6400001;
for (int i = u + 1; i < x; i++)
ret = min(ret, min(dp[u][v][i][y][1] + dp[i][v][x][y][times - 1],
dp[u][v][i][y][times - 1] + dp[i][v][x][y][1]));
for (int i = v + 1; i < y; i++)
ret = min(ret, min(dp[u][v][x][i][1] + dp[u][i][x][y][times - 1],
dp[u][v][x][i][times - 1] + dp[u][i][x][y][1]));
return ret;
}
void solve()
{
memset(sum, 0, sizeof(sum));
for (int i = 7; i >= 0; i--)
for (int j = 7; j >= 0; j--)
sum[i][j] = chessBoard[i][j] + sum[i + 1][j] + sum[i][j + 1] - sum[i + 1][j + 1];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
for (int k = 0; k < 9; k++)
for (int l = 0; l < 9; l++)
for (int times = 0; times <= n; times++)
dp[i][j][k][l][times] = 6400001;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
for (int k = i + 1; k < 9; k++)
for (int l = j + 1; l < 9; l++)
dp[i][j][k][l][1] = blockSum(i, j, k, l);
for (int partBlockNum = 2; partBlockNum < n; partBlockNum++) //part k times
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
for (int k = i + 1; k < 9; k++)
for (int l = j + 1; l < 9; l++)
dp[i][j][k][l][partBlockNum] = minSeparation(i, j, k, l, partBlockNum);
double ans = minSeparation(0, 0, 8, 8, n);
printf("%.3lf\n", sqrt(ans / n));
}
int main()
{
while (scanf("%d", &n) != EOF) {
init();
solve();
}
return 0;
}