POJ 1191 棋盘分割

这篇博客介绍了如何利用动态规划解决POJ 1191题——棋盘分割问题。通过求解各块分值平方和最小,找到最优分割策略。博主分享了状态转移方程和编程实现细节,讨论了数据类型选择对结果的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目连接:http://poj.org/problem?id=1191

解题思路:

棋盘分割一块之后,剩余的棋盘的分割和原问题类似,只是规模变小了。棋盘分割问题可以采用动态规划方法解决。

对方差公式进行化解,得到σ^2=1/n∑xi^2 - x^2

可知,要使方差最小,只需使∑xi^2最小即可,即各块分值平方和最小。平均值 x是个固定的数,跟分割的方式没有关系,

首先定义状态:dp[k][x1][y1][x2][y2],表示左上角坐标(x1,y1)到右下角坐标(x2,y2)区域的棋盘经过k次分割后,各块分值平方和的最小值。则状态转移方程为:

dp[k][x1][y1][x2][y2] = min{

dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2], (x1 <= t < x2)

dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2], (x1 <= t < x2)  //竖切

dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2], (y1 <= t < y2)

dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2] (y1 <= t < y2) //横切

}

dp[0][x1][y1][x2][y2]表示左上角坐标(x1,y1)到右下角坐标(x2,y2)区域的棋盘的分值和平方

有了状态转移方程后,就很容易写出代码了,开始dp数组使用long double,提交后WA,改成double后就AC了,很奇怪。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <iomanip>

using namespace std;

int data[9][9];
int sum[9][9];
double dp[14][9][9][9][9];

//返回左上角坐标(x1,y1)到右下角坐标(x2,y2)区域的棋盘的分值和平方 
double count(int x1, int y1, int x2, int y2)
{
	double ans = (double)(sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
	
	return ans*ans;
}

int main()
{
	int n, total=0;
	//输入数据 
	cin>>n;
	for(int i=1; i<=8; ++i)
	  for(int j=1; j<=8; ++j)
	  {
			cin>>data[i][j];
			//sum[i][j]表示棋盘(1,1)到(i,j)区域的累计分值 
			sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + data[i][j];
			//total表示整个棋盘的分值之和 
			total += data[i][j];
		}

	//初始化dp数组 
	for(int x1=1; x1<=8; ++x1)
	 for(int y1=1; y1<=8; ++y1)
	  for(int x2=x1; x2<=8; ++x2)
		  for(int y2=y1; y2<=8; ++y2)
			 dp[0][x1][y1][x2][y2] = count(x1,y1,x2,y2);
		  
	//自底向上计算dp数据 
	for(int k=1; k<n; ++k)
	 for(int x1=1; x1<=8; ++x1)
	  for(int y1=1; y1<=8; ++y1)
	   for(int x2=x1; x2<=8; ++x2)
		  for(int y2=y1; y2<=8; ++y2)
		  {
				int t;
				dp[k][x1][y1][x2][y2] = (double)(1<<30);
				for(t=x1; t<x2; ++t)
				{
					dp[k][x1][y1][x2][y2] = min(dp[k][x1][y1][x2][y2], dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2]);
					dp[k][x1][y1][x2][y2] = min(dp[k][x1][y1][x2][y2], dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2]);
				}
				
				for(t=y1; t<y2; ++t)
				{
					dp[k][x1][y1][x2][y2] = min(dp[k][x1][y1][x2][y2], dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2]);
					dp[k][x1][y1][x2][y2] = min(dp[k][x1][y1][x2][y2], dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2]);
				}
			}
	
	//计算方差平方 
	double ans = dp[n-1][1][1][8][8]*1.0/n - ((double)total*1.0/n)*((double)total*1.0/n); 
	
	//输出方差,精确到小数点后三位 
	cout<<setprecision(3)<<fixed<<sqrt(ans)<<endl;

	return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值