c语言side输出空心正方形,回溯法--正方形(蛋糕切分)问题

这篇博客介绍了一种利用深度优先搜索策略解决积木拼成正方形的问题。通过一维数组cols和side分别记录已占用的行数和不同边长的积木数量,从矩阵左上角开始尝试按列填充,逐步检查每个正方形是否能放入。如果所有小正方形的面积总和不等于目标正方形面积,则无法完成拼接。博客提供了C++代码实现,并给出了搜索过程中的剪枝优化技巧。
摘要由CSDN通过智能技术生成

对每组数据输出一行"yes"或"no",表示能否用全部积木拼成一个正方形.

解题思路:

鉴于积木的个数不多,可以采用深度优先的方式进行搜索;

首先,要组成的大正方形的表示:采用一维数组cols[41]来表示,其中cols[i]表示第i列中已占用的行的行号加1,即可用的第一行行号。为什么为41,是因为要组成的正方形可能的最大边长41,而我们让下标从1开始。

然后,输入的积木的边的保存:还是采用一维数组side[11],其中side[i]表示边长为i的边的数目,这里的11同上,也是由于最大为10,而我们从side[1]开始。

搜索的策略,很容易想到可以从矩阵的左上角开始填,每次按列从左边向右填,左边的填完了才能天右边的,显然这样是正确的。

优化与剪枝,首先,所有小蛋糕的面积和不等于要拼的正方形的面积,则没必要填。可以每次按正方形从大到小开始填,如果该大小的正方形填不进去,则其他相同大小的正方形就没有必要所了,回溯后,原先大小的正方形也没必要填,因为正方形最大是10,所以从10开始选,一直递减,看有没有该大小的正方形,能不能放入,如果放不下则选择比其小的正方形即可,否则,该正方形的数目减一,修改该矩形,搜索下一个正方形,如果该拼法行不通,则回溯。

C++代码如下:

#include

#include

using namespace std;

int

s; //s,表示要组成的正方形的边长

int

n; //n(1<=n<=16),表示积木的个数

int

side[11]; //side[i]用来保存边长为i的小正方形的个数,这里约定正方形的边长最大为10

int

cols[41]; //cols[i]表示第i列已经被占用的行数+1,即最小可用行起始

vector result;

int backTrace(int a)

{

int selectCol, minRow, i, j, flag;

if (a == n) return 1;

minRow = 41;

for (i = 1; i <= s; ++i)

if (cols[i] <

minRow)

{

selectCol =

i;

minRow =

cols[i];

}

for (i = 10; i > 0; --i)

{

if (side[i]

&& selectCol-1+i <=

s && minRow-1+i <=

s)

{

flag =

1;

for (j =

selectCol; j < selectCol + i; ++j)

if

(cols[j] > minRow)

{

flag

= 0;

break;

}

if

(flag)

{

for

(j = selectCol; j < selectCol + i; ++j) cols[j] +=

i;

side[i]--;

if

(backTrace(a + 1)) return 1;

else

{

for

(j = selectCol; j < selectCol + i; ++j) cols[j] -=

i;

side[i]++;

}

}

}

}

return 0;

}

int main()

{

int cases = 0;

int i, temp;

cin>>cases;

int area;

while (cases > 0)

{

cases--;

cin>>s>>n;

memset(side, 0,

sizeof(side));

area = 0;

for (i = 0; i <

n; ++i)

{

cin>>temp;

side[temp]++;

area += temp

* temp;

}

if (area != s * s)

result.push_back(0);

else

{

for (i = 1; i

<= 40; ++i) cols[i] = 1;

int rr =

backTrace(0);

result.push_back(rr);

}

}

for (i = 0; i < result.size();

++i)

if (result[i])

cout<

else

cout<

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值