uva:307 - Sticks(dfs + 剪枝)

题目:307 - Sticks


题目大意:给出不多于50个的木棍块,这个木棍块又是由一些等长的木棍被随意切割而成的,所以现在题目要求要找出这些木棍块能拼出的最小的木棍长度。


解题思路:这题就是dfs + 剪枝。

剪枝的地方有4处:

1.木棍的长度一定会是这些木棍块加起来的长度的约数,并且木棍是被切割的,所以一定不能小于最长木棍块长。

2.将木棍块从大到小排序,找能否拼成某个长度木棍的木棍块先从最长的开始,这样的话能过比较快的判断出这个木棍的长度是否能够被拼出。

3.如果在同一个状态下,就是在同一个递归,同一个for()循环中,发现s[i]这个长度的木棍块没办法和后面的木棍块拼出你想要的木棍的话,那么如果s[i - 1]和s[i]相等的话就可以直接不判断这个木棍块了。

4.如果是在刚开始选木棍块来拼木棍的话(可以是拼完一条了,准备下一条开始的时候),发现当前选的这个和别的都拼不上,那么最后的话这个木棍块一定会剩下,所以这种情况就可以直接返回,节约时间。

注意这里如果拼起来大于你所要的木棍长度的话还得继续判断,并不能直接都否决,因为可能是和这几个木棍块组不成,和别的就可以组成了也说不定。唯独是碰到上面情况4的时候,所以这里的dfs就要从是否==你要的长度和是否< 你要的长度的两种情况来写。具体看代码吧。还有这题有个坑(别人和我说的)数组开55竟然过不了,得开105的说,不理解。。。


#include
#include
#include
using namespace std;

const int N = 105;
int n, s[N], vist[N];
//剪枝  1.如果在剩余相同的木棍的状况下,s[i]的情况不能满足要求s[i - 1] == s[i]直接跳过
int dfs(int init, int ans, int x, int c){
	
	if(c == n)
		return 1;
	for(int i = x; i >= 1; i--){
		
		if(vist[i])
			continue;
		if(init + s[i] < ans){

			vist[i] = 1;
			if(dfs(init + s[i], ans, i - 1, c + 1))	
				return 1;
			vist[i] = 0;
			while(i - 1 > 0 && s[i] == s[i - 1]){   

				i--;
			}
			
		}
		else if(init + s[i] == ans){

				vist[i] = 1;
				if(dfs(0, ans, n, c + 1))
					return 1;
				vist[i] = 0;
				return 0;
		}
		if(init == 0)  
			return 0;	
		
	}

	return 0;
}


int main() {

	while(scanf("%d", &n), n){

		int i, max = 0;
		for(i = 1; i <= n; i++){

			scanf("%d", &s[i]);	
			max += s[i];
		}
		sort(s + 1, s + n + 1);
		for(i = n; i >= 1; i--){

			memset(vist, 0, sizeof(vist));
			if(max % i == 0 && s[n] <= (max / i) && dfs(0, max / i, n, 0)){

				printf("%d\n", max / i);
				break;
			}
		}
	}
	return 0;
}


你可能感兴趣的:(暴力求解,UVA,暴力-回溯-难)