知识点:深搜,剪枝
首先我们知道剪枝一共有5大类,除了记忆化,这个一般是学动态规划的时候学的,剩余4个是,优化搜索顺序,排除等效冗余,最优化剪枝,可行性剪枝,本题主要是练习排除等效冗余,这个方面的剪枝是比较经典的
1 首先是优化搜索顺序,先搜大的再搜小的,有一种说法是小的容易见缝插针,大的则不行,我自己的感觉是大的挤占的空间更大,剩余的空间小那么选择也少,总之不管那种说法,感觉上,是先搜大的再搜小的更好,一般优化搜索顺序这块儿都是从大往小搜,也有可能是我做题少还没有遇见别的情况
2 人为规定,一个大木棒里面放的小木棍的编号是递增的,因为一个木棒里面放的两个相同的木棍顺序不同,此时属于一种情况,所以我们需要记录上一个放的木棍的编号在函数的参数里面
3 长度相等的小木棍具有等效性,也就是我们正在填某个木棒,如果当前的小木棍的分支不满足情况,那么后面长度相同的小木棍的分支同样是不满足的,就不要进入
4 空的大木棒具有等效性,这里的等效性指的是失败时候具有等效性,也就是我们往一个空的木棒里面放了一个小木棍,下面的分支是失败的,那么后面空的木棒也都不会搜索成功,这里就直接不需要搜后面的木棒了,直接返回
5 这个是最难的,说是用了贪心的思想,也就是如果一个木棒拼上一个木棍之后长度正好满足,继续向下搜下一个木棒,下面的分支搜索失败了,那么这里就直接返回,因为用一个木棍拼好,下面的分支都要失败,再往后想要拼好这个空缺,那么注定需要多个小木棍,那么多个小木棍就算拼好,后面的分支也是注定要失败的
#include
using namespace std;
const int N = 70;
int n, a[N], sum, cnt, flag, vis[N];
void dfs(int stick, int cur, int last) {
if (flag) return;
if (stick == cnt) {
flag = 1;
return;
}
int fail = 0;
for (int i = last + 1; i <= n; i++) {
if (vis[i] || cur + a[i] > sum / cnt || a[i] == fail) continue;
vis[i] = 1;
if (cur + a[i] == sum / cnt) dfs(stick + 1, 0, 0);
else dfs(stick, cur + a[i], i);
vis[i] = 0;
if (!flag) fail = a[i];
if (!flag && (!cur || cur + a[i] == sum / cnt)) return;
}
}
int main() {
while (cin >> n && n) {
flag = sum = 0;
memset(vis, 0, sizeof(vis));
int beg = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
beg = max(beg, a[i]);
}
sort(a + 1, a + n + 1);
reverse(a + 1, a + n + 1);
for (int i = beg; i <= sum; i++) {
if (sum % i) continue;
cnt = sum / i;
dfs(0, 0, 0);
if (flag) { cout << i << '\n'; break; }
}
}
return 0;
}