POJ 1011 Sticks (DFS + 剪枝)
题目地址:http://poj.org/problem?id=1011
题目大意
给出n个小木棒,组合成若干长度最短棍子
解题思路
- 首先将木棒从大到小排序
- dfs(k, l), k是还剩多少木棒没用,l是当前没组装成功的棍子还需多长木棒
- 剪枝(核心)
- 选取能被木棒总长度能整除的棍子长度
- 如果当前选取木棒的长度比所需的长度大,剪枝
- 如果剩余的木棒总长度还没所需的长度大,剪枝
- dfs每一次开始选取木棒,要从上一次选取的后面开始
- 对于相等的木棒,如果一次不成功,就无需再次尝试
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100], v[100], n, len, cnt;
// 正在拼第stick根原始木棒(已经拼好了stick-1根)
// 第stick根木棒的当前长度为cab
// 拼接到第stick根木棒中的上一根小木棍为last
bool dfs(int stick, int cab, int last) {
// 所有原始木棒已经全部拼好,搜索成功
if (stick > cnt) return true;
// 第stick根木棒已经拼好,去拼下一根
if (cab == len) return dfs(stick + 1, 0, 1);
int fail = 0;
// 小木棍长度递减(从last开始枚举)
for (int i = last; i <= n; i++)
if (!v[i] && cab + a[i] <= len && fail != a[i]) {
v[i] = 1;
if (dfs(stick, cab + a[i], i + 1)) return true;
fail = a[i];
v[i] = 0; // 还原现场
if (cab == 0 || cab + a[i] == len) return false;
}
return false; // 所有分支均尝试过,搜索失败
}
int main() {
while (cin >> n && n) {
int sum = 0, val = 0, m = 0;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
if (x <= 50) {
a[++m] = x;
sum += a[m];
val = max(val, a[m]);
}
}
n = m;
sort(a + 1, a + n + 1);
reverse(a + 1, a + n + 1);
for (len = val; len <= sum; len++) {
if (sum % len) continue;
cnt = sum / len; // 原始木棒长度为len,共cnt根
memset(v, 0, sizeof(v));
if (dfs(1, 0, 1)) break;
}
cout << len << endl;
}
}