南邮算法分析与设计实验3 回溯法 (3)

Georgetook sticks of the same length and cut them randomly until all parts became atmost 50 units long. Now he wants to return sticks to the original state, but heforgot how many sticks he had originally and how long they were originally.Please help him and design a program which computes the smallest possibleoriginal length of those sticks. All lengths expressed in units are integersgreater than zero.

Input

Theinput contains blocks of 2 lines. The first line contains the number of sticksparts after cutting, there are at most 64 sticks. The second line contains thelengths of those parts separated by the space. The last line of the filecontains zero.

Output

Theoutput should contains the smallest possible length of original sticks, one perline.

Sample Input

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

Sample Output

6 5

Source

Central Europe 1995

题目大意:给若干根切断的木条,用完所有所给木条拼成n根等长的原始木条,求所拼原始木条的最短长度

题目分析:回溯法 + 剪枝 ,本题限制时间为1000ms,剪枝不到位绝对会超时,以下列出几条不可少的剪枝
1. 设有n根木条枚举能拼成的长度为sum/n~1。这里n必须是sum的约数否则无解,找到了则为最优解。由于均分的越多长度就越短
2.按长度从大到小排序,最长的一根长度必定小于sum/n,否则无解。从长的開始取,由于长的灵活度比較低,之后便于剪枝。
3.若搜索时某两根的长度同样,第一根没取那第二根也不会取
4.每次取的木条加上取之前的长度要小于sum/n
5.假设当前长度cur为0,当前取的最长木条为stick[i],结果stick[i]拼接失败则我们能够直接退出搜索,当前情况无解,由于假设某次当前 最长的木条没被选上,则之后木条数更少了它更不会被选上,由于我们是依照降序排列的


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int const MAX = 64; int vis[MAX]; int stick[MAX]; int num, one, div_num; bool cmp(int a, int b) { return a > b; } //cur代表当前某根木条的拼接长度,other代表未用木条。count代表拼成的根数 bool DFS(int cur, int other, int count) { if(count == div_num) //若拼成的根叔等于份数,则找到返回 return true; for(int i = other; i < num; i++) { //若当前供选长度与前一个相等且前一个未使用则不考虑当前这根 if(i && !vis[i-1] && stick[i] == stick[i-1]) continue; if(cur + stick[i] <= one && !vis[i]) { //若加上小于原始长度将其标记为已使用继续搜索 if(cur + stick[i] < one) { vis[i] = 1; if(DFS(cur + stick[i], i + 1, count)) return true; //若之前方案不可行。将当前这根标记为未使用 vis[i] = 0; //这是个非常重要的剪枝。若当前选择的最长长度得不到解,该方案必定无解 if(cur == 0) return false; } //若加上等于原始长度将其标记为已使用拼成一根,開始拼下一根 if(cur + stick[i] == one) { vis[i] = 1; if(DFS(0, 0, count + 1)) return true; //若之前方案不可行,将当前这根标记为未使用 vis[i] = 0; //这是个非常重要的剪枝,若之前的DFS(0,0,count+1)有一个不满足 //则该方案必定无解 return false; } } } return false; } int main() { int sum; while(scanf("%d",&num) != EOF && num) { sum = 0; memset(vis,0,sizeof(vis)); for(int i = 0; i < num; i++) { scanf("%d",&stick[i]); sum += stick[i]; } sort(stick, stick+num, cmp); //从大到小排序 //从份数最多的枚举起,则找到一组解就能够退出。由于它必为最短 for(int i = num; i > 0; i--) { if(sum % i == 0) //每根原始木条的长度必须是整数 { one = sum / i; //一根原始木条的长度 //若当前的原始木条短于最长的切断的木条长度则原始木条值非法 if(stick[0] > one) continue; else { div_num = i; //份数 if(DFS(0,0,0)) //找到一个可行解就退出 break; } } } printf("%d\n",one); } }


内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzjpwf.html