您的位置:首页 > 其它

HDU - 1455 Sticks(深搜+剪枝)

2016-05-17 23:56 211 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1455

说点题外话。这几天再次看搜索,找到一种新的理解方式,果然处理问题方便多了。

搜索的本质:利用递归实现很多个 for 循环,在每一次循环中不断筛选判断。

常见的搜索:

1,枚举搜索  (HDU 2533 - 八皇后问题,UVa 524 - 素数环,UVa 129 - Krypton Factor)

2,路径搜索  (HDU 1455 - Sticks,HDU 1010 - Tempter of the Bone)

上面举例的都是比较经典的一些例题了,比较难得可以推荐 USACO - 2.1 The Castle(http://train.usaco.org/usacoprob2?a=cV5k5rRXOz6&S=castle),会涉及好多搜索过程中的优化问题,找到最优解之类的。

简单说一下我所理解的上述两种搜索。

第一种:

枚举搜索: 特点是,每一步搜索即每一个递归即每一个for循环都可以枚举当前情况,根据情况进行筛选判断。

eg:按字典序输出n个数的全排列,每一步都可枚举,每一步都可判断能否枚举(即已经形成的排列集合里面无该数字),最后即可输出所有情况。

这种题 dfs 函数一般定义为 void 类型,因为每一步你就可以判断能否使用该枚举数,需要的是 return ;到上一步即可。

第二种:

路径搜索: 特点是无法确定的枚举出所有情况,充满着不确定因素。唯一能做的是顺藤摸瓜,一步一步去延伸,一步一步去扩展,一步一步去往深处走,这也正是 dfs 的本质。eg:走迷宫问题。知道起始点,规定步数,问在规定步数内是否可以到达。显然你无法把每一条路都枚举去进行判断。情况太多太复杂,靠人脑无法解决。这时候就可以考虑路径搜索这种情况。虽然不能将每一条路都可以枚举出来,但是可以将每一步都枚举出来(依据是:当前步可行),顺着这一条路走到头,这就是一种情况了。具体的可以看这个blog的题
HDU  - 1455 Sticks,下面有代码。  

这种题 dfs 一般定义为 bool 类型,因为你需要 return ture or false 来告诉之前的决策:" 哦!此路不通,你要从上一步开始重新找路了"。    

这种问题通常不难理解,只要接触5道左右基本可以掌握。难就难在如何进行剪枝,因为递归开销非常大。  这里有一种我理解的剪枝 ,和本题很类似。倘若某一决策涉及到之前使用过的决策且之前使用过的决策不能达到目的,那么就进行剪枝。大致描述是这样,不是很清楚,具体还要看题啊。QAQ~

总而言之,我最大的收获是:将搜索理解成n重for循环,然后每一重筛选判断。

#include <iostream>
#include <cstring>
#include <algorithm>
#define MAX 55
using namespace std;
/*************************************************************************************************************
题意:讲的大致是几根原本长度相同的木棒,然后被某人当出气筒剪啊剪啊,剪成好几段,
然后,好吧,这时间一长记性就差了,忘了原来这堆木棒的长度
思路:
1,将给定木棒排序,原木棒长度 <= 木棒和 && 原木棒长度 >= 这一堆木棒的最大值。
2,在区间内进行深搜即可,这几天深搜理解的也比较到位了。
3, 29行的剪枝绝对不能少!一般人想不到
*************************************************************************************************************/

int a[MAX],visit[MAX],n;

bool dfs(int SUM,int sum,int cur)
{
if(cur == n && sum == SUM)    return true;
if(sum == SUM)    sum=0;

for(int i = n;i >= 1;i --){
if(!visit[i] && sum+a[i] <= SUM){
visit[i]=1;
if(dfs(SUM,sum+a[i],cur+1))
return true;
visit[i]=0;

if(sum == 0)        //剪枝:如果当前搜索时,前边的长度为0,而第一根没有成功的使用,
//      说明第一根始终要被废弃,所以这种组合必定不会成功
return false;
while(a[i] == a[i-1])
i--;
}
}
return false;
}

int main()
{
while(cin>>n,n != 0)
{
int SUM=0,i;

for(i = 1;i <= n;i ++){
cin>>a[i];
SUM+=a[i];
}
sort(a+1,a+n+1);

for(i = a
;i <= SUM;i ++){
if(SUM%i)    continue;

memset(visit,0,sizeof(visit));
if(dfs(i,0,0)){
cout<<i<<endl;
break;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: