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