1. 程式人生 > >POJ 1191 棋盤分割 (區間DP,記憶化搜索)

POJ 1191 棋盤分割 (區間DP,記憶化搜索)

bool for ring def bsp sca printf namespace http

題面

思路:分析公式,我們可以發現平均值那一項和我們怎麽分的具體方案無關,影響答案的是每個矩陣的矩陣和的平方,由於數據很小,我們可以預處理出每個矩陣的和的平方,執行狀態轉移。

設dp[l1][r1][l2][r2][k]是矩陣l1,r1,l2,r2切割k次的最小值,我們可以枚舉是橫著切還是豎著切執行狀態轉移。

代碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;

int dp[10][10][10][10][16], sum[10][10][10][10];
bool v[10][10][10][10][16];
int a[10][10];

void init() {
	for (int l1 = 1; l1 <= 8; l1++) 
		for (int r1 = 1; r1 <= 8; r1++)
			for (int l2 = l1; l2 <= 8; l2++)
				for (int r2 = r1; r2 <= 8; r2++) {
					int ans = 0;
					for (int i = l1; i <= l2; i++)
						for (int j = r1; j <= r2; j++) {
							ans += a[i][j]; 
						}
					sum[l1][r1][l2][r2] = ans * ans;
				}
}

int solve(int l1, int r1, int l2, int r2, int k) {
	if (v[l1][r1][l2][r2][k]) return dp[l1][r1][l2][r2][k];
	if (k == 1) {
		return dp[l1][r1][l2][r2][k] = sum[l1][r1][l2][r2];
	}
	int ans = INF;
	for (int i = l1; i < l2; i++) {
		ans = min(ans, min(solve(l1, r1, i, r2, k - 1) + sum[i + 1][r1][l2][r2], solve(i + 1, r1, l2, r2, k - 1) + sum[l1][r1][i][r2]));
	}
	for (int i = r1; i < r2; i++) {
		ans = min(ans, min(solve(l1, r1, l2, i, k - 1) + sum[l1][i + 1][l2][r2], solve(l1, i + 1, l2, r2, k - 1) + sum[l1][r1][l2][i]));
	}
	v[l1][r1][l2][r2][k] = 1;
	return dp[l1][r1][l2][r2][k] = ans;
}
int main() {
	int n, tot = 0;
	scanf("%d", &n);
	for (int i = 1; i <= 8; i ++) {
		for (int j = 1; j <= 8 ; j++) {
			scanf("%d", &a[i][j]);
			tot += a[i][j];
		}
	}
	init();
	solve(1, 1, 8, 8, n);
	double ans = sqrt(dp[1][1][8][8][n] * 1.0 / n - (tot * 1.0 / n) * (tot * 1.0 / n));
	printf("%.3f\n", ans);
} 

  

POJ 1191 棋盤分割 (區間DP,記憶化搜索)