dfs剪枝(排除等效冗余)
一、剪枝优化
1.优化搜索顺序:
有限考虑分支较少的搜索方式,常见的比如从大到小排序
2.排除等效冗余
:排除等效的情况,本题就是很好的例子,稍后解释
3.可行性剪枝
4.最优性剪枝
二、本题的排除等效冗余
1.如果是木棒的第一段就搜索失败了,则一定搜不到方案
2.如果是木棒的最后一段搜索失败了,则一定搜索不到方案
3.如果木棒的长度为k的一段搜索失败了,那么后续同样长为k的一段同样也会搜索失败
剪枝3
比较明显,下面证明剪枝1
和剪枝2
例如:\(1,1,1,3,.....\)
对于剪枝1
,我们用反证法:假设某个木棒的第一段搜索失败了,存在解决方案
假如开始将 \(3\) 作为木棒 \(a\) 的第一段搜索失败了,但存在一种合法方案,那么 \(3\) 肯定会放在后面的木棒 \(b\) 中,但我们交换木棒 \(a\) 和木棒 \(b\) 的搜索顺序并不会影响答案,因此这与 \(3\) 作为木棒 \(a\) 的第一段搜索失败矛盾。
对于剪枝2
,我们用反证法:假设某个木棒的最后一段搜索失败了,存在解决方案
假如将 \(3\) 作为木棒 \(a\) 的最后一段搜索失败了,但存在一种合法方案,那么肯定会存在 \(k(k>1)\) 根木棒(不妨为 \(1,1,1\) )使得这 \(k\) 根木棒作为 \(a\) 的最后阶段成功,并且 \(3\) 作为木棒 \(b\) 的一段成功,同理我们可以将 \(3\) 看作木棒 \(b\) 的最后一段,而交换木棒 \(a\) 和木棒 \(b\) 的搜索顺序并不会影响答案,故矛盾。
三、Code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 70;
int n, w[N];
bool st[N]; // 因为我们需要剪枝实现跳跃,因此需要st标记
// 当前已经恢复了cnt根木棍,每根木棍的长为length
// 当前正在恢复的木棍长为cur
// 下次dfs从start开始遍历w数组
// 所有木棍长度之和为sum
int sum, length;
bool dfs(int cnt, int cur, int start)
{
if(cnt * length == sum) return true;
if(cur == length) return dfs(cnt + 1, 0, 0);
for(int i = start; i < n; i ++ )
{
if(!st[i] && cur + w[i] <= length)
{
st[i] = true;
if(dfs(cnt, cur + w[i], i + 1)) return true;
st[i] = false;
// 走到这里说明第i根木棍放在这没有合法方案
// case1
if(!cur) return false;
// case2
if(cur + w[i] == length) return false;
// case3,下一次for从j开始,但下一次dfs仍从start开始
int j = i;
while(j < n && w[j] == w[i]) j ++ ;
i = j - 1;
}
}
return false;
}
int main()
{
while(cin >> n, n)
{
memset(st, false, sizeof st);
sum = 0;
for(int i = 0; i < n; i ++ ) cin >> w[i], sum += w[i];
sort(w, w + n, greater<int>()); // 从大到小排序,优化搜索顺序
for(length = 1; length <= sum; length ++ )
{
if(sum % length == 0 && sum / length <= n && dfs(0, 0, 0))
{
cout << length << endl;
break;
}
}
}
return 0;
}