uva 307 Sticks(回溯剪枝)

uva 307 Sticks

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help
him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input

The input file contains blocks of 2 lines. The first line contains the number of sticks parts after cutting. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output file contains the smallest possible length of original sticks, one per line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

题目大意:本来有m根长为l等长的木棒,将其分别切成若干段之后(可能切,可能没切),忘记了原来的长度l,求最短的长度l。

解题思路:简单的回溯会超时,这题对剪枝的要求很高,也是看了别人的题解才过的。

(转)

①先将木棒长度从大到小进行排序,这样便于后面的选择和操作,是后面一些剪枝算法的前提。

②在枚举原木棒长度时,枚举的范围为max与sum/2之间,如果这个区间内没有找到合适的长度,那么最后原木棒的长度只能是sum。

③枚举的原木棒的长度只能是sum的约数。

④在深搜过程中,如果当前木棒和前一个木棒的长度是一样的,但是前一个木棒没有被选上,那么这个木棒也一定不会被选上。

⑤在深搜过程中,如果当前是在拼一根新木棒的第一截,但如果把可用的最长的一根木棒用上后不能拼成功的话,那么就不用再试后面的木棒了,肯定是前面拼的过程出了问题。

⑥在深搜过程中,如果当前可用的木棒恰好能补上一根原木棒的最后一截,但用它补上之后却不能用剩下的木棒完成后续的任务,那么也不用再试后面的木棒了,肯定是前面拼的过程出了问题。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
int s[100], n, sum, L, vis[100], cnt, ans, num, len;
int cmp(int a, int b) {
	return a > b;
}
int DFS(int l, int d, int c) {
	if (l == L) { //拼凑木条或原木条等于要求长度
		c++; //完成木条数加一
		if (c == num) { //完成木条数等于当前需求木条数,获取最小木条数
			if (ans > c) ans = c;
			return 1;
		}
		else {
			for (d = 0; vis[d]; d++); //找寻没使用过的木条
			vis[d] = 1;
			if (DFS(s[d], d + 1, c)) return 1;
			vis[d] = 0;
		}
	}
	else {
		for (int i = d; i < n; i++) {
			if (!vis[i] && s[i] <= L - l) {
				if (i != 0 && s[i] == s[i - 1] && !vis[i - 1]) continue; //如果前一个木条没有访问过,下一个相同的木条也无需访问
				vis[i] = 1;
				if (DFS(l + s[i], i + 1, c)) return 1;
				vis[i] = 0;
				if (s[i] == L - l) return 0; //后续木棒不能再用,有剩余
			}
		}
	}
	return 0;
}
int main() {
	while (scanf("%d", &n) == 1, n) {
		memset(vis, 0, sizeof(vis));
		cnt = 0;
		sum = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d", &s[i]);
			sum += s[i];
		}
		sort(s, s + n, cmp);
		ans = 0xFFFFFFFF;
		for (L = s[0]; L <= sum / 2; L++) { //找出因子,进行DFS
			if (sum % L != 0) continue;
			else {
				num = sum / L;
				if (DFS(0, 0, 0)) break;
			}
		}
		if (L <= sum / 2) {
			printf("%d\n", L);
		}
		else printf("%d\n", sum);
	}
	return 0;
}
时间: 2024-08-15 07:16:17

uva 307 Sticks(回溯剪枝)的相关文章

poj 1011 Sticks ,剪枝神题

木棒 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 118943 Accepted: 27429 Description 乔治拿来一组等长的木棒.将它们随机地砍断.使得每一节木棍的长度都不超过50个长度单位.然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度.请你设计一个程序,帮助乔治计算木棒的可能最小长度.每一节木棍的长度都用大于零的整数表示. Input 输入包括多组数据,每组数据包括两

UVA - 10003Cutting Sticks(递推)

题目:UVA - 10003Cutting Sticks(递推) 题目大意:给根木棍长度l,现在要锯这根木棍,给出n个锯点,求怎样锯才能使得开销最小.例如 长度为10的木棍, 锯点2 4 7,那么如果按照这个顺序 , 首先显示由长度位10的木头先锯了2 ,开销就加10,然后锯完现在有[0,2]和[2,10]长度分别为2 ,8的木棍,现在要在4这个位置锯木头,就是在长度为8的木头上锯4这个位置,这样就加上8,然后又有长度为[2,4][4,10]的木头,最后要锯7的话,就需要开销加上6.所以开销就是

UVA10317- Equating Equations(回溯+剪枝)

题目链接 题意:给出一个式子,但这个式子不一定是等式,在'+','-'符号位置不变的情况下,重新排列数字的位置,使其成为等式,如果可以的话,输出其中一种排列方式. 思路:我们将等号右边的数全部移动到等号右边,例如a+b-c=d-e,移动后变成a+b+e-(c+d)=0,也就是a+b+e=c+d,所以当式子可以变化成等式时,所有数的和必然是偶数.那么问题可以转化为在n个数中找出m个数(m的值为等号左边的整数的数量),使m个 数的和为从和的一半. #include <iostream> #incl

HDU1016 Prime Ring Problem (回溯 + 剪枝)

题意: 给你一个数字N(N <= 20),要求你把这N个数组成一个环,环内的数字不能重复,左右相邻的两个的和是素数.给出最后的答案. 思路: 利用回溯剪枝算法,N个数,每个数有N种状态,枚举这N个状态,枚举过程中剪枝优化. 代码: #include <cstdio> #include <iostream> #include <cstring> #include <cstring> #include <cmath> #include <

[POJ 1011]Sticks(DFS剪枝)

Description George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were original

HDU1010 Tempter of the Bone(回溯 + 剪枝)

题意: 输入一个 N * M的迷宫,这个迷宫里'S'代表小狗的位置,'X'代表陷阱,‘D’代表门,‘.’代表可行走的地方,小狗每次可以选择往周围的四个方向行走,问这个小狗能否正好T步找到门. 思路: 利用回溯 + 剪枝,这道题剪枝特别重要. 剪枝一: 可以把图看成这样: 1 0 1 0 10 1 0 1 01 0 1 0 10 1 0 1 01 0 1 0 1 则假设从点 a(i + j,横纵坐标之和) 走到点 b(i + j) ,如果 a 和 b 的奇偶性相同,那么从 a 到 b 必须是偶数步

uva10400 - Game Show Math(回溯+剪枝)

题目:uva10400 - Game Show Math(回溯+剪枝) 题目大意:给出N个数,并且给出一个目标数值,要求用上面的数字(全部),并且顺序不能乱,然后用+-*/这些操作,问最终能不能得到目标数值.这里要注意给出的数会在[-32000,32000]之间, 并且要用除法的时候,只有在能整除的时候才能用.并且中间计算结果不能超过[-32000,32000]范围.如果超过这个操作不能做. 解题思路:回溯加剪枝,将每一层计算的结果都保存下来,如果在同一层发现值出现过,并且之前计算发现这样往后是

UVa 140 Bandwidth(DFS 回溯 剪枝)

题意  有一个无向图  对于其所有顶点的一个排列 称一顶点与所有与其有边的其它顶点在排列中下标差的最大值为这个顶点的带宽   而所有顶点带宽的最大值为这个排列的带宽   求这个图带宽最小的排列 枚举排列  ans记录当前找到的最小带宽  枚举过程中一旦出现带宽大于ans的也就不用再扩展了  这样枚举完就得到了答案 #include<cstdio> #include<cstring> using namespace std; const int N = 50; int n, ans,

[算法专题] 深度优先搜索&amp;回溯剪枝

1. Palindrome Partitioning https://leetcode.com/problems/palindrome-partitioning/ Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s. For example, given s = "aab&