POJ1011-Sticks-经典搜索题详解优化

Sticks

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

解题思路

1.一开始的思路是遍历木棒能组成的所有长度,然后进行许多判断;发现判断过程很复杂,做不了,就换了个思路;
2.遍历所有可能的木棒长度len,并得到木棒的个数cnt;

len满足 sum%len==0;
cnt=sum/len;

3.得出DFS的终止条件:

拼接成功的木棒 > cnt 或者 无法继续拼接

4 .然后就是这题的精华:各种剪枝与优化

1.木棍应从长到小遍历;
2.限制拼接一根木棒的木棍长度是递减的,因为 y+x == x+y;(但每次拼接一根木棒时都要从1 ~ n遍历一遍)
3.记录最近一个拼接的木棍长度,如果搜索失败,以后不再向当前木棒(stick)中添加相同长度的木棍;
4.如果当前木棍作为最后一部分使得木棒拼接成功(cab+num[i]==len),并且接下来拼接之后木棒的分支(DFS(stick+1,0,1)==false),那么就可以判断当前分支失败,直接回溯,即不再向当前木棒中添加相同长度的木棍。

总结

(吐槽一句:POJ不支持万能头文件~~)
1.DFS的剪枝是一个很重要的方面,也是很复杂的;可以先把整体的搜索框架码出来,再进行剪枝处理;
2.第4点是最难理解的,需要自己敲一遍才能理解;

代码展示

//#include<bits/stdc++.h>
#include<iostream>
#include<cmath>
#include<string.h>
#include<algorithm>
using namespace std;
/*
DFS+剪枝 
遍历所有木棒的可能长度,直到符合条件 
*/
int num[70];
bool vis[70];
int n,cnt,len;
bool DFS(int stick,int cab,int last){
	if(stick>cnt)return true;
	if(cab==len)return DFS(stick+1,0,1);
	int fail=0;//剪枝: 避免访问多次相同长度的木条  
	for(int i=last;i<=n;i++){
		if(!vis[i]&&cab+num[i]<=len&&fail!=num[i]){
			vis[i]=1;
			fail=num[i];
			if(DFS(stick,cab+num[i],i+1))return true;//为什么是 i+1
			vis[i]=0; 
			if(cab==0||cab+num[i]==len)return false;
			//剪枝:1.当一根木条不能作为木棒的第一块,那么其他的也不行
			//      2.当用一根木条无法拼接成功木棒时,其他若干根也不行 (作为木棒的最后一块时) 
		}
	}
	return false;
}
int main(){
	while(true){
		memset(vis,0,sizeof(vis));
		cin>>n;
		int val=0;
		if(n==0)break;
		for(int i=1;i<=n;i++){
			cin>>num[i];
			val+=num[i];
		}
		sort(num+1,num+n+1);
		reverse(num+1,num+1+n);
		for(int i=num[1];i<=val;i++){
			if(val%i)continue;
			cnt=val/i;
			len=i;//当前选择的木棒长度
			if(DFS(1,0,1)){
				cout<<len<<endl;//当前拼接第i根木棒,长度为j,选择的是dik个小木棒 
				break;
			}
		}
	} 
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高冷小伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值