一道簡單的搜尋題,我卻做了兩天,真是失敗,從剛開始,沒有思路,到想到方法,再到如何?,從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;
}