[coursera算法初步]递归_棋盘分割问题

将一个8*8的棋盘进行如下分割:
将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,
再将剩下的部分继续如此分割, 这样割了(n-1)次后,
连同最后剩下的矩形棋盘共有n块矩形棋盘.
(每次切割都只能沿着棋盘格子的边进行)
切割示例
注意粗体字“再将剩下的部分继续切割”,也就是说,被割出去的那一块是不允许再被切割的。

原棋盘上每一格有一个分值,
一块矩形棋盘的总分为其所含各格分值之和
现在需要把棋盘按上述规则分割成 n 块矩形棋盘,
并使各矩形棋盘总分的均方差最小

输入
第1行为一个整数n (1 < n < 15)
第2行至第9行每行为8个小于100的非负整数, 表示棋盘上
相应格子的分值
每行相邻两数之间用一个空格分隔
输出
仅一个数, 为σ(均方差) (四舍五入精确到小数点后三位)
公式如下:这里写图片描述
为了使优化目标简化,将公式里的内容展开,化简后得到:
这里写图片描述
xi是每一个矩形格的总分。

接下来就是切割方法的问题了。我们先把目光集中到一块矩形,由于需要一刀把一个矩形切成两块,所以切法是很有限的。就像一般计算机的问题那样,我们把所有情况列举出来,该问题就会变得十分清晰。
这里写图片描述
而切法,也就是如图四种所示。阴影部分表示切掉的一块,空白部分表示剩下的部分(也就是n-1块)。

这道题的目标是求方差最小的切法,经过前面的化简之后,目标变成了就各个矩形总分平方和最小的办法。因此,最主要的问题就是构造计算平方和的函数。
这里写图片描述
因此这道题是用递归来解决的,把n块的问题分解为n-1块与1块相加,然后一步步递归,每一次递归只需要从四种切法当中选一个最大的切法(当然,从哪个坐标开始切还是需要遍历的)

代码如下:

#include<iostream>
using namespace std;

int s[9][9];//每个格子的分数
int sum[9][9];//从(1,1)到(i,j)的分数之和,目的是方便各个矩形块总分的计算
int res[15][9][9][9][9];//fun的记录表

int calSum(int x1,int y1,int x2,int y2){
return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}//计算矩形块总分
int fun(int n,int x1,int y1,int x2,int y2){
int t,a,b,c,e,MIN=10000000;//先把min值设得很大
if(res[n][x1][y1][x2][y2] !=-1)
return res[n][x1][y1][x2][y2];
//下面是递归出口,n为1的情况
if(n==1) {
t=calSum(x1,y1,x2,y2);
res[n][x1][y1][x2][y2]=t*t;
return t*t;
}
//竖着切的情况
for(a=x1;a<x2;a++) {
c=calSum(a+1,y1,x2,y2);//割掉右边的面积
e=calSum(x1,y1,a,y2);//割掉左边的面积
t=min(fun(n-1,x1,y1,a,y2)+c*c, fun(n-1,a+1,y1,x2,y2)+e*e);
if(MIN>t) MIN=t;
}
//横着切的情况
for(b=y1;b<y2;b++) {
c=calSum(x1,b+1,x2,y2);
e=calSum(x1,y1,x2,b);
t=min(fun(n-1,x1,y1,x2,b)+c*c, fun(n-1,x1,b+1,x2,y2)+e*e);
if(MIN>t) MIN=t;
}
res[n][x1][y1][x2][y2]=MIN;
return MIN;
}

int main() {
memset(sum, 0, sizeof(sum));
memset(res, -1, sizeof(res)); //初始化记录表,memset就别管他了,就是全设为0,全设为-1
int n;
cin>>n;
for (int i=1; i<9; i++)
for (int j=1, rowsum=0; j<9; j++) {
cin>>s[i][j];
rowsum +=s[i][j];
sum[i][j] += sum[i-1][j] + rowsum;
}
double result = n*fun(n,1,1,8,8)-sum[8][8]*sum[8][8];
cout<<setiosflags(ios::fixed)<<setprecision(3)<<sqrt(result/(n*n))<<endl;
return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值