木棒拼接 poj1011 搜索+剪枝 遞歸實現

木棒拼接,POJ上的1011

題目:http://poj.org/problem?id=1011

 

        經典的搜索+剪枝問題。這題我已經做第三遍了,感覺依舊很暈。說一下,自己對搜索的感覺。以前寫搜索算法,總會不自覺的用到棧。比如,迷宮問題,通過壓棧保存路徑,通過彈棧進行回退。還要設置個標誌,保證“不走來時路”。

 

        看了大牛們寫的code後,有種醍醐灌頂的感覺:遞歸形式的寫法真簡潔!因爲這種寫法把壓棧彈棧的工具交給了complier, 所以代碼顯得格外清爽。但是,玫瑰雖香,身上帶刺。清爽簡潔並不代表簡單易寫。這是,函數的參數、返回值都要反覆斟酌。要不斷的問自己:函數需要幾個參數、有沒有返回值、遞歸終止條件是什麼。

 

        針對本題,拼接的過程是一個DFS,即:不斷拿木棒嘗試,拼好一個再拼下一個。需要知道的參數:當前拼接已獲得的木棒長度、已經拼接成功的木棒數、當前正在嘗試的木棒編號。拼接的過程需要判斷,所以函數返回bool.

 

        在用棧實現時,回退就是不斷的彈棧。這裏,回退就比較隱晦,只是把已經設置的標記再復原一下。而且,遞歸調用時也很抽象。只有像binary tree上寫遞歸一樣,假設遞歸調用的部分結果已知,然後才能繼續寫後面的代碼。

 

本題的另一個關鍵在剪枝,可以用於剪枝的條件:

1.      對木棍長度按從大到小排序。

2.      計算所有木棍的總長度,只搜索能被總長度整除的數

3.      從木棍的最大長度開始搜索

4.      保存上次嘗試的木棒長度,如果拼接失敗,則相同長度不用重複嘗試

5.      當嘗試的木棒標號回退到0時,直接返回false


Problem: 1011		User: 3109034010
Memory: 264K		Time: 16MS
Language: C++		Result: Accepted


#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

//***********************常量定義*****************************

const int MAX_NUM = 70;


//*********************自定義數據結構*************************




//********************題目描述中的變量************************

int stickNum;
int stickLen[MAX_NUM];


//**********************算法中的變量**************************

int sumLen;
bool used[MAX_NUM];


//***********************算法實現*****************************

bool FindStick( int curId, int curLen, int curNum, int targetLen )
{
	if( curNum == sumLen / targetLen )
	{
		return true;
	}
	if( curLen == targetLen )
	{
		return  FindStick( 0, 0, curNum+1, targetLen );
	}
	else
	{
        int i;
    	int pre = 0;
    	for( i=curId; i<stickNum; i++ )
    	{
    		if( pre != stickLen[i] && ( curLen + stickLen[i] <= targetLen ) && !used[i] )
    		{
    			pre = stickLen[i];
                used[i] = true;
                //下面這一句很抽象
                //當FindStick == true時, stickLen[i]和後續的木棒都可以拼接成功                 
    			if( FindStick( i+1, curLen + stickLen[i], curNum, targetLen ) )
    			{
                    break;
                }
                
                used[i] = false;
                //這句也不好理解
                //爲什麼不是 i == 0 
                if( curId == 0 )
                {
                    return false;
                }            
    		}
    	}
    	if( i >= stickNum )				
    	    return false;    
        else
            return true; 
    }	
}


//************************main函數****************************

int main()
{
	//freopen( "in.txt", "r", stdin );	
	while( cin >> stickNum, stickNum )	
	{	
        sumLen = 0;
        memset( used, false, sizeof(used) );        
		for( int i=0; i<stickNum; i++ )
		{
			cin >> stickLen[i];             			
			sumLen += stickLen[i];
		}
		sort( stickLen, stickLen + stickNum, greater<int>() );		
        
        int x;
        for( x=stickLen[0]; x<sumLen; x++ )
    	{
    		if( sumLen % x == 0 )
    		{    			
    			if( FindStick( 0, 0, 0, x ) )
    			{
    			    break;
    			}
    		}
    	}
		cout << x << endl;		
	}	 
	return 0;
}


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章