UVA307 小木棍 Sticks

知识点:深搜,剪枝

首先我们知道剪枝一共有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;
}

你可能感兴趣的:(加入题解目录题解,算法)