【DFS+剪枝】POJ 1011 Sticks

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

题目的大致意思是,告知一开始有若干木棍的长度相等的木棍,然后把他们切成n个不同长度的小木棍,这n个不同长度的小木棍的长度已知,要你求最开始的木棍长度最小可以为多少。

二、题目思路以及AC代码

这题一开始看到的思路也很清晰,就是,首先,由于一开始木棍长度都相等,所以这个长度必定是给定的n个长度和的一个因子,然后解决方法就是对每个因子进行遍历,然后每次遍历求解一次dfs,看是否可以达到这个长度,因为因子是从小到大排列,所以找最先找到的那个就可以了。

但是思路是思路,AC是AC,这题要想AC,可远没有思路这么简单,需要进行剪枝。主要就是两部分剪枝。

  • 不要重复dfs相同长度的木块,在找寻一个目标长度的木块时,如果已经发现长度为x的不能使结果成立,那么就不要再用这个长度了。
  • 如果在新凑一个目标长度的木块时,此时第一个长度的木块不能使目标成立,那么直接返回不可能即可。(因为假设你已经凑好了2个木块,刚要凑第三个,然后你取到sticks[i],发现后面的木块任意组合也无法满足要求,那么这种方法就是不可能的,直接返回false即可,如果不返回false,你就会将这一块的下一块作为第三块的第一个,这时已经不可能成立了,因为你要凑的是若干个完整的木块,对于每个小木板的地位是相同的,这个不行,其他的肯定也不可以,如果你要问为什么是第一块,那么我的解释是,如果你中间的某块发现后面任意组合也不行,那你换一块,说不定就可以把当前这个大木块满足,然后后面也可能会满足。尽量去理解吧,确实有点难)

然后其实就没有了,这样,就可以把一个原本TLE的暴力dfs,变成一个20ms的AC代码。

下面给出AC代码:

#include <iostream>
#include <algorithm>
#define MAXN 70
using namespace std;

int n;
int sticks[MAXN];
int vis[MAXN];
int factor[30];
int fsize;
int cnt = 0;

void init() {
	for (int i=0;i<MAXN;i++){
		sticks[i] = 0;
		vis[i] = false;
	}
	for (int i = 0; i < 30; i++) {
		factor[i] = 0;
	}
	fsize = 0;
	cnt = 0;
}

bool allVis() {
	for (int i = 0; i < n; i++) {
		if (!vis[i]) return false;
	}
	return true;
}

bool dfs(int cur, int m, int pre, int num) {
	if (cur == m) {
		if (num == n) return true;
		if (dfs(0, m, 0, num)) return true;
		return false;
	}
	else if (cur > m) return false;

	for (int i = pre; i < n; i++) {
		if (vis[i]) continue;
		if (sticks[i] > m - cur) continue;
		if (i > 0 && !vis[i - 1] && sticks[i - 1] == sticks[i]) continue;
		vis[i] = true;
		if (dfs(cur + sticks[i], m, i + 1, num + 1))
			return true;
		vis[i] = false;
		if (pre == 0)
			return false;
	}

	return false;
}

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

int main()
{
	while (cin >> n) {
		if (!n) break;
		init();

		int max = -1;
		int sum = 0;
		for (int i = 0; i < n; i++) {
			cin >> sticks[i];
			sum += sticks[i];
			if (max < sticks[i])
				max = sticks[i];
		}
		
		sort(sticks, sticks + n, cmp);

		for (int i = 1; i <= sum; i++) {
			if (sum % i == 0) factor[fsize++] = i;
		}

		int idx = 0;
		for (int i = 0; i < fsize; i++) {
			if (factor[i] >= max) {
				idx = i;
				break;
			}
		}

		while (idx < fsize && !dfs(0, factor[idx], 0, 0)) {
			idx++;
		}
		
		cout << factor[idx] << endl;
	}

    return 0;
}

如果有问题,欢迎大家指正!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值