code vs 1711 棋盘分割 (dp)

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

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
输入
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出
仅一个数,为O'(四舍五入精确到小数点后三位)。
样例输入
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
样例输出
1.633
来源
Noi 99

题解:dp

我们其实可以将均方差公式化简。

sqrt((x1^2+x2^2+...+xn^2-(x1+x2+...+xn)^2/n)/n)

在这个式子中只有x1^2+x2^2+...+xn^2是变量,其他的都是常量。我们要使均方差最小,其实就是最小化x1^2+x2^2+...+xn^2的值。

因为每次只能切割下一个矩形,并且要求剩下的部分仍然是矩形。那么每次就只有上下左右四种切割方式。然后考虑进行记忆化搜索,dfs(x,y,x1,y1,n) (x,y)表示剩下矩形的左上角坐标 (x1,y1) 剩下矩形的右下角坐标 n表示还需要切割的次数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 20
#define inf 1000000000
using namespace std;
int n,f[N][N][N][N][N],a[N][N],num;
int sum[N][N];
int calc(int x)
{
	return x*x;
}
int dfs(int x,int y,int x1,int y1,int n)
{
	if (x>x1||y>y1) return inf;
	if (n==1) {
		f[x][y][x1][y1][n]=calc(sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1]);
		return f[x][y][x1][y1][n];
	}
	if (f[x][y][x1][y1][n]!=-1) return f[x][y][x1][y1][n];
	int ans=inf;
	for (int i=x;i<=x1;i++) 
	 {
	 	int t=dfs(i+1,y,x1,y1,n-1)+calc(sum[i][y1]-sum[i][y-1]-sum[x-1][y1]+sum[x-1][y-1]);
	 	int t1=dfs(x,y,i,y1,n-1)+calc(sum[x1][y1]-sum[i][y1]-sum[x1][y-1]+sum[i][y-1]);
	 	ans=min(t,ans); ans=min(t1,ans);
	 }
	for (int i=y;i<=y1;i++)
	 {
	 	int t=dfs(x,i+1,x1,y1,n-1)+calc(sum[x1][i]-sum[x-1][i]-sum[x1][y-1]+sum[x-1][y-1]);
	 	int t1=dfs(x,y,x1,i,n-1)+calc(sum[x1][y1]-sum[x1][i]-sum[x-1][y1]+sum[x-1][i]);
	    ans=min(t,ans); ans=min(t1,ans);
	 }
	f[x][y][x1][y1][n]=ans;
	return f[x][y][x1][y1][n];
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=8;i++)
	  for (int j=1;j<=8;j++)
	 	 scanf("%d",&a[i][j]),num+=a[i][j];
	for (int i=1;i<=8;i++)
	 for (int j=1;j<=8;j++)
	  sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	memset(f,-1,sizeof(f));
	int ans=dfs(1,1,8,8,n); 
	double ans1=(double)ans-(double)calc(num)/(double)n;
	ans1/=(double)n;
	printf("%.3lf\n",sqrt(ans1));
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值