Sticks (剪枝)

深度搜索与剪枝技巧在解决问题中的应用
本文探讨了如何使用深度优先搜索(DFS)结合剪枝技术来解决复杂问题。作者分享了两种不同的实现方式,其中一种是个人原创,另一种是从网络上获取的灵感。在第二種方法中,特别提到了在求和为0的情况下的讨论对于避免超时至关重要。
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
Input
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
Output
The output should contains the smallest possible length of original sticks, one per line.
Sample Input
### 代码功能分析 提供的代码是使用深度优先搜索(DFS)来解决木棍拼接问题。问题的核心是给定一组小木棍,要将它们拼接成若干根长度相同的原始木棍,找出能拼接成的原始木棍的最小可能长度。 代码流程如下: 1. 输入小木棍的数量和每根小木棍的长度。 2. 计算所有小木棍的总长度`tot`和最长小木棍的长度`maxlen`。 3. 从`maxlen`开始到`tot`,依次尝试可能的原始木棍长度`len`。 4. 对于每个可能的`len`,检查`tot`是否能被`len`整除,如果不能则跳过。 5. 若能整除,计算需要拼接成的原始木棍数量`num`,并调用`dfs`函数进行拼接尝试。 6. `dfs`函数尝试将小木棍拼接成`num`根长度为`len`的原始木棍,若成功则返回`true`,否则返回`false`。 7. 找到第一个能成功拼接的`len`,输出该长度。 ### 代码优化建议 1. **输入排序**:在输入小木棍长度后,对其进行降序排序。这样在拼接时优先使用长木棍,能更快地排除一些不可能的情况。 2. **剪枝优化**: - **可行性剪枝**:若当前拼接的木棍长度加上下一根小木棍长度超过`len`,则跳过该小木棍。 - **最优性剪枝**:若当前拼接的木棍数量超过了已经找到的最优解,提前终止搜索。 - **重复元素剪枝**:若当前小木棍与前一个小木棍长度相同且前一个小木棍未被使用,则跳过该小木棍。 3. **避免重复搜索**:记录每次拼接第一根小木棍的编号,确保拼接顺序严格递增。 ### 优化后的代码 ```cpp #include<bits/stdc++.h> using namespace std; const int N = 80; int n, sticks[N], num, len, tot, maxlen, first[N]; bool vis[N]; // 深度优先搜索函数 bool dfs(int cur, int sum, int last) { if (cur > num) return true; if (sum == len) return dfs(cur + 1, 0, 0); for (int i = last + 1; i <= n; i++) { if (!vis[i] && sum + sticks[i] <= len) { vis[i] = true; if (sum == 0) first[cur] = i; if (dfs(cur, sum + sticks[i], i)) return true; vis[i] = false; // 重复元素剪枝 if (sum == 0) return false; while (i + 1 <= n && sticks[i + 1] == sticks[i]) i++; } } return false; } int main() { while (cin >> n && n) { tot = maxlen = 0; memset(vis, 0, sizeof(vis)); for (int i = 1; i <= n; i++) { cin >> sticks[i]; maxlen = max(maxlen, sticks[i]); tot += sticks[i]; } // 对小木棍长度进行降序排序 sort(sticks + 1, sticks + n + 1, greater<int>()); for (len = maxlen; len <= tot; len++) { if (tot % len != 0) continue; num = tot / len; if (dfs(1, 0, 0)) break; } cout << len << endl; } return 0; } ``` ### 代码解释 1. **排序**:在`main`函数中,使用`sort(sticks + 1, sticks + n + 1, greater<int>())`对小木棍长度进行降序排序。 2. **重复元素剪枝**:在`dfs`函数中,当一个小木棍尝试拼接失败后,跳过后面长度相同的小木棍,避免重复搜索。 3. **可行性剪枝**:在`dfs`函数中,若`sum + sticks[i] > len`,则跳过该小木棍。 4. **最优性剪枝**:在`dfs`函数中,若`cur > num`,说明已经拼接的木棍数量超过了需要的数量,提前终止搜索。 ### 复杂度分析 - **时间复杂度**:最坏情况下为$O(2^n)$,但通过剪枝优化,实际运行时间会大大减少。 - **空间复杂度**:$O(n)$,主要用于存储小木棍的长度和访问标记。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值