POJ1020-Anniversary Cake【解題報告|DFS】

題目鏈接

題目大意

有一塊邊長爲BoxSize的正方形的大蛋糕,現在給出n塊不同尺寸的正方形的小蛋糕的邊長,問是否能把大蛋糕按恰好切割爲這n塊小蛋糕,要求每塊小蛋糕必須爲整塊。

解題思路

有技巧的DFS

可以把大蛋糕想象爲一個蛋糕盒子,然後往裏面裝小蛋糕。

裝蛋糕時遵循以下原則:

自下而上,自左至右;

即先裝好盒子底部,再繼續往上層裝,且裝每一層時都靠左邊放蛋糕;

大蛋糕優先裝,因爲小蛋糕靈活度比較高。

只要把問題變換爲上述問題,我想對深搜比較熟悉的同學也會馬上得到思路了,這個只是很簡單的DFS思路。

但是本題的難點不在於怎樣去DFS,而是每放入一個蛋糕後,怎樣去標記盒子已經放有蛋糕的位置?

我初始的做這題時,因爲看到數據規模不大(Max_n=16,Max_size=10,那麼大蛋糕最大也就404040*40),於是我把尺寸爲BoxSize的盒子劃分爲BoxSizeBoxSize11BoxSize*BoxSize個1*1的格子,每放入一個大小爲size的蛋糕,就用一個二重循環去標記sizesizesize*size的格子。

最後是毫無懸念地TLE了。

看了別人的方法,發現或分格子的思路是正確的,但應該“按列標記”。不但把盒子看做多個111*1個格子,也把小蛋糕看做多個111*1的單位,建立一個一維數組col[BoxSize]col[ BoxSize ],每放入一個蛋糕,則去記錄每列的格子被填充的數目。

例如在第2~4列放入了一個size=3size=3的小蛋糕,那麼col[2]+=3,col[3]+=3,col[4]+=3col[2]+=3, col[3]+=3, col[4]+=3。有同學會問,爲什麼行不用計數?要是放入蛋糕後,該蛋糕底部出現部分懸空怎麼處理?這個情況是不會出現的,因爲當前DFS遵循先把底部放滿原則,要是出現懸空,則會回溯。

更具體的處理方法請看程序註釋。

//Memory Time 
//208K  32MS 
 
#include<iostream>
using namespace std;
 
int BoxSize;      //盒子尺寸
int n;            //蛋糕的總個數
int SizeNum[11];  //各種尺寸的蛋糕個數
int col[41];      //把盒子縱行分割成BoxSize*BoxSize個1*1大小的小格子
                  //col[i]記錄第i列被填充了的格子數
 
bool DFS(int FillNum)   //FillNum:已放入盒子的蛋糕數
{
	if(FillNum==n)
		return true;
 
	/*尋找格子數被填充最少的列,靠左優先*/
	int min=50;
	int prow;
	for(int i=1;i<=BoxSize;i++)
		if(min>col[i])
		{
			min=col[i];
			prow=i;
		}
 
	/*枚舉各種尺寸的蛋糕自下而上地放入盒子*/
    for(int size=10;size>=1;size--)
	{
		if(!SizeNum[size])
			continue;
 
		//檢查尺寸爲size的蛋糕放入盒子時在縱向和橫向是否越界
		if(BoxSize-col[prow]>=size && BoxSize-prow+1>=size)
        {
			//檢查盒子從第prow列到第prow+size-1列,共size列的寬度wide中
			//是否每列剩餘的空間都足夠放入高度爲size的蛋糕
            int wide=0;
            for(int r=prow;r<=prow+size-1;r++)
			{
				if(col[r]<=col[prow])  //比較各列的"填充數"
				{    //注意,這裏若比較"未填充數"BoxSize-col[r]<size會TLE
					wide++;       //雖然兩個條件等價,但確實計算了3秒左右,不知何故
					continue;
				}
				break;
			}
 
            if(wide>=size)
            {
				int r;
                //放入尺寸爲size的蛋糕
				SizeNum[size]--;
				for(r=prow;r<=prow+size-1;r++)
					col[r]+=size;
			
				if(DFS(FillNum+1))
					return true;
 
				//回溯
				SizeNum[size]++;
				for(r=prow;r<=prow+size-1;r++)
					col[r]-=size;
            }
        }
	}
    return false;
}
 
int main(void)
{
	int test;
	cin>>test;
	for(int t=1;t<=test;t++)
	{
		memset(SizeNum,0,sizeof(SizeNum));
		memset(col,0,sizeof(col));
 
		cin>>BoxSize>>n;
 
		int cnt=0;   //記錄size>(BoxSize/2)的蛋糕個數
		int area=0;  //計算所有蛋糕的面積之和
		for(int i=1;i<=n;i++)
		{
			int size;
			cin>>size;
			area+=size*size;
			SizeNum[size]++;
 
			if(size>BoxSize/2)
				cnt++;
		}
 
		if(cnt>1 || area!=BoxSize*BoxSize)
		{
			cout<<"HUTUTU!"<<endl;
			continue;
		}
 
		if(DFS(0))
			cout<<"KHOOOOB!"<<endl;
		else
			cout<<"HUTUTU!"<<endl;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章