一道简单的搜索题,我却做了两天,真是失败,从刚开始,没有思路,到想到方法,再到如何实现,从N次wrong后,到TLE,再剪枝优化,最后Accept,可谓一路辛苦,其中也多亏大牛的提示。
题意是把一个大蛋糕(边长为E),能否切成给定s(s<15)个边长为Ei(1=<Ei<=10)的小蛋糕,所有的蛋糕都是正方形.题目的规模不是很多,应该是深搜来拼,刚开始一点头绪也没有,以前只搜过点。
1.保存状态很重要,这是遇到的第一个问题,想法是用一个矩形来存储,用一个表示列的数组d[i](这个刚开始没想到),d[i]表示第i列没填蛋糕的行数。
2.搜索的策略,很容易想到可以从矩阵的左上角开始填,每次按列从左边向右填,左边的填完了才能天右边的,显然这样是正确的。
3.优化与剪枝,首先,所有小蛋糕的面积和不等于要拼的蛋糕的面积,则没必要填。如果直接从左上角填这样效率很低的,可以每次按蛋糕从大到小开始填,可以想到这样很高效的。当时根据以上两点写了后,调试出正确答案后,提交依然超时,这样,如果不可以不可以拼成的话,就会执行 piece!次,看了discuss之后,才发现如果该大小的蛋糕填不进去,则其他相同大小的蛋糕就没有必要所了,回溯后,原先大小的蛋糕也没必要填,刚开始,我是用一个数组存这些切成的蛋糕的边长,然后对其递减排序,每次按数组下标填入,那么如果该大小的蛋糕放不下,则下标就应该跳rec[](表示该大小的蛋糕数),但这样不好控制,调试了很长时间都没成功。其实很简单,没必要排序,每次选最大的蛋糕,因为蛋糕最大是10,所以从10开始选,一直递减,看有没有该大小的蛋糕,能不能放入,如果放不下则选择比其小的蛋糕即可,否则,该蛋糕的数目减一,修改该矩形,搜索下一块蛋糕,如果该拼法行不通,则回溯。
这其中,反映出我很差的代码能力,以及对深度搜索认识的薄弱,以及对问题认识不够深刻,不够全面,以后这方面要努力了!加油!
Source Code
Problem: 1020 User: zhouxc
Memory: 200K Time: 47MS
Language: C++ Result: Accepted
#include "iostream"
using namespace std;
int test,cake,piece;
int p[20],d[50],rec[20];
bool wasted,cpt;
int Find_Min()
{
//返回最左列第一个有插完的一列的行号,以后每次会从该列插入
蛋糕,这一列前面的列都插完了,而后面的还没插
for(int i=0;i<cake;i++)
{
if(d[i]!=0)
return i;
}
return -1;// 返回-1表示每一列都插完了,则可以正好分配
}
// dfs切每一块蛋糕,其中cunt表示切了的蛋糕数
bool Cake_dfs(int cunt) {
// 最左边未填慢的列,下一块蛋糕将要开始从该列紧挨着已填的填入
int rr=Find_Min();
// printf("%d %d/n",rr,cunt);
if(rr==-1) //表示正好填好
return true;
//每次填时尝试取最大的蛋糕
else
{ //size表示蛋糕的大小,因为蛋糕最大不超过10
for(int size=10;size>0;size--)
{
if(rec[size]) //如果存在该大小的蛋糕
{
cpt=true;
//判断该大小的蛋糕是否可填入
for(int j=rr;j<size+rr;j++)
{
if(d[j]<size)
{
cpt=false;
break;
}
}
if(cpt)
{
//把该大小的蛋糕填入,并修改填入后的d值
for(int i=rr;i<size+rr;i++)
d[i]-=size;
//该大小的蛋糕数减一
rec[size]--;
//搜索填入下一块蛋糕
if(Cake_dfs(cunt+1))
return true;
//当该方案行不通时回溯,尝试另一种填法
rec[size]++;
for(int i=rr;i<size+rr;i++)
d[i]+=size;
}
}
}
}
return false;
}
int main()
{
scanf("%d",&test);
while(test--)
{
int result=0;c=0;
scanf("%d%d",&cake,&piece);
memset(rec,0,sizeof(rec));
for(int i=0;i<piece;i++)
{
scanf("%d",&p);
result+=p*p;
rec[p]++; //边长为p的蛋糕的数
}
//初始化,d[i]表示第i列,已填入蛋糕的行数,刚开始都没填
for(int i=0;i<cake;i++)
d[i]=cake;
wasted=true;
//当所有的的蛋糕的面积和等于要拼成的蛋糕面积时,开始拼,否则一定不能拼成
if(result==cake*cake)
wasted=!Cake_dfs(0);
if(!wasted)
puts("KHOOOOB!");
else
puts("HUTUTU!");
}
return 0;
}