木棒拼接 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;
}


 

 

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