小木棍-搜索剪枝

小木棍,多么经典的题目啊,几年都拿出来做一下,今年终于感觉有点明白了(真为中年妇女的智商着急啊:()

题解抄的luogu题解,修改了以前抄的代码,终于ac了.

主要难点在于如何进行dfs的剪枝可以二分答案(不二分也不会超时,我的代码没有二分),dfs看看这个答案可不可行虽然数据很小,直接dfs也是会超时的,所以需要加入一些剪枝。

1.从max(最大木棍长度,总木棍长度/要分的段数)为左面,所有木棍长度为右面二分答案。
2.木棍长度要排序,方便每次dfs从小到大,为避免重复搜索(比如说2 3 4 和2 4 3)定义一个pos,每次枚举从pos开始,这也是为什么排序的原因之一。//我原来没有这个剪枝,得66分.
3.如果现在搜索状态的剩余长度和二分答案的长度相等,还搜不出答案,说明有木棍无解,直接break跳出循环。//这条没用,没写二份答案
4.如果现在搜索状态的剩余长度和循环搜索到木棍的长度相等,那么下一次dfs的状态如3,还搜不出答案,说明有木棍无解,直接break跳出循环。
5.如果这个长度的木棍搜不出答案,相同长度的也搜不出答案。

#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[110];//存小棒长大小最好大于64
int visited[110];//标记小棒是否被用
int n;//小棒个数
bool cmp( int a,int b) {
	return a>b;
}

int DFS(int len,int pos,int remains_len,int num) {
	if(remains_len==0&&num==0)//都拼完了
		return len;
	if(remains_len==0) { //又拼接起一根
		remains_len=len;
		pos=0;
	}
	for(int i=pos; i<n; i++)    {
		if(visited[i]==1) continue;
		if(remains_len>=a[i]) {
			visited[i]=1;
			if(DFS(len,i+1,remains_len-a[i],num-1)) return len;
			visited[i]=0;
			if(a[i]==remains_len||len==remains_len) //a[i]已经太长,后面的更长,剪枝
				break;
			while(a[i]==a[i+1])//已经试过的长度,不用再重复试。
				i++;
		}
	}
	return 0;
}
int main() {
	while(cin>>n&&n) {
		int sum=0;//所有小棒长度
		int len,k;
		for(int i=0; i<n; i++) {
			cin>>a[i];
			if(a[i]>50) {
				i--;
				n--;
			} else
				sum+=a[i];
		}
		sort(a,a+n,cmp);
		for(len=a[0]; len<=sum; len++) {
			memset(visited,0,sizeof(visited));
			if(sum%len==0) {
				k=DFS(len,0,0,n);
				if(k)  break;
			}
		}
		cout<<k<<endl;
	}
	return 0;
}


### 问题分析 洛谷 P1120 小木棍问题是一个典型的深度优先搜索(DFS)与剪枝优化结合的组合搜索问题。目标是将一组长度各异的小木棍拼接成若干根长度相同的木棒,要求每根木棒由若干根原始木棍组成,并且每根木棒的长度一致。 由于木棍数量可能较多,直接穷举所有组合会导致指数级的时间复杂度,因此必须通过剪枝策略进行优化,以提高搜索效率。 --- ### 解题思路 #### 1. **预处理与基本条件判断** - 首先计算所有木棍的总长度 `sum`。 - 若目标长度 `length` 不是 `sum` 的约数,则无法均分,直接跳过该 `length` 的尝试。 - 所有木棍按长度从大到小排序,以减少搜索分支,优先尝试长木棍。 #### 2. **DFS 搜索策略** - 使用 DFS 递归地尝试将木棍组合成目标长度的木棒- 每次递归尝试选择一个未使用的木棍加入当前木棒,若当前木棒长度等于目标长度,则开始下一根木棒的拼接。 - 使用一个数组或布尔数组记录哪些木棍已被使用。 #### 3. **剪枝优化** 以下为关键剪枝策略: - **剪枝 1:长度筛选** 若当前木棍长度加上当前木棒已拼接长度超过目标长度,则跳过该木棍- **剪枝 2:跳过重复长度** 若当前木棍长度与前一根未使用的木棍长度相同,则跳过,避免重复组合。 - **剪枝 3:首根失败剪枝** 若当前木棒的第一根木棍无法成功拼接,则直接回溯,因为后续组合也无法成功。 - **剪枝 4:末根失败剪枝** 若当前木棒的最后一根木棍(刚好凑成目标长度)失败,则整个方案失败。 - **剪枝 5:可行性剪枝** 利用计数排序思想,若当前长度的木棍无法构成某根木棒的剩余长度,则跳过该类长度的木棍--- ### 代码实现(C++) ```cpp #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N = 65; int stick[N], used[N]; int n, total_len, target_len, cnt; bool dfs(int now_len, int start, int num) { if (num == 0) return true; if (now_len == target_len) { return dfs(0, 0, num - 1); } int prev = -1; for (int i = start; i < n; ++i) { if (used[i] || stick[i] + now_len > target_len) continue; if (stick[i] == prev) continue; // 跳过重复长度 used[i] = 1; prev = stick[i]; if (dfs(now_len + stick[i], i + 1, num)) return true; used[i] = 0; if (now_len == 0) return false; // 首根失败 if (now_len + stick[i] == target_len) return false; // 末根失败 } return false; } int main() { cin >> n; int sum = 0; for (int i = 0; i < n; ++i) { cin >> stick[i]; sum += stick[i]; } sort(stick, stick + n, greater<int>()); // 降序排序 for (int len = stick[0]; len <= sum / 2; ++len) { if (sum % len != 0) continue; target_len = len; cnt = sum / len; fill(used, used + n, 0); if (dfs(0, 0, cnt)) { cout << len << endl; return 0; } } cout << sum << endl; return 0; } ``` --- ### 总结 解决 P1120 小木棍问题的关键在于合理设计 DFS 搜索结构,并结合多种剪枝策略减少无效搜索路径。通过排序、跳过重复、首末剪枝等方法,可以显著提升算法效率,从而在合理时间内完成大规模数据的求解。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值