POJ 1011 Sticks (DFS + 剪枝)

题目地址:http://poj.org/problem?id=1011

题目大意

给出n个小木棒,组合成若干长度最短棍子

解题思路

  • 首先将木棒从大到小排序
  • dfs(k, l), k是还剩多少木棒没用,l是当前没组装成功的棍子还需多长木棒
  • 剪枝(核心)
    • 选取能被木棒总长度能整除的棍子长度
    • 如果当前选取木棒的长度比所需的长度大,剪枝
    • 如果剩余的木棒总长度还没所需的长度大,剪枝
    • dfs每一次开始选取木棒,要从上一次选取的后面开始
    • 对于相等的木棒,如果一次不成功,就无需再次尝试

AC代码

#include
#include
#include
#include
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;
	}
}

你可能感兴趣的:(POJ 1011 Sticks (DFS + 剪枝))