poj 1191 棋盘分割 动态规划

备战最后一次问求,这两周要多做些题目了。今晚做了一道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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值