P1120. 小木棍

考点

  • DFS
  • 剪枝

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>
using namespace std;
const int maxn = 70;
bool vis[maxn];
int n, C, L, a[maxn];

// 第cnt根火柴的长度为len,从lst开始枚举
bool dfs(int cnt, int len, int lst) {
// 全部成功枚举
if (cnt > C) return true;
// 当前火柴拼凑成功,进入下一根
if (len == L) return dfs(cnt + 1, 0, 1);
int fail = 0;
for (int i = lst; i <= n; ++i) {
// 当前木棍没被用过,且在范围内,且没被标记失败
if (!vis[i] && len + a[i] <= L && a[i] != fail) {
vis[i] = 1;
if (dfs(cnt, len + a[i], i + 1)) return true;
vis[i] = 0;
// i木棍拼不到当前火柴里,所以长度相同的木棍全部跳过
fail = a[i];
// 当前是火柴的第一根木棍,都没办法满足L
// 可以拼完当前火柴,但是剩余木棍无法满足L
if (len == 0 || len + a[i] == L) return false;
}
}
return false;
}

bool cmp(int &a, int &b) { return a > b; }

int main() {
while (~scanf("%d", &n) && n) {
int sum = 0, mx = 0;
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), sum += a[i], mx = max(mx, a[i]);
sort(a + 1, a + 1 + n, cmp);
for (L = mx; L <= sum; ++L) {
if (sum % L) continue;
C = sum / L;
memset(vis, 0, sizeof(vis));
if (dfs(1, 0, 1)) {
cout << L << endl;
break;
}
}
}
return 0;
}

思路

直接上图。