Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 142684 | Accepted: 33684 |
Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 5
问题:
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棒的长度都不超过50个长度单位。然后他又想把这些木棒恢复到为裁截前的状态,但忘记了木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棒的长度都用大于零的整数表示
输入:由多个案例组成,每个案例包括两行。第一行是一个不超过64的整数,表示裁截之后共有多少节木棒。第二行是经过裁截后,所得到的各节木棒的长度。在最后一个案例之后,是零。
输出:为每个案例,分别输出木棒的可能最小长度。每个案例占一行。
思路: 给你n 木棍碎片,要求还原原木棍,且原木棍等长,问最短长度是多少。
1.先求sum总和,以最长的一根长度开始枚举长度,每次长度加一,枚举+深搜
2.寻找一个可以sum%len=0的len,必须先对木棒从大到小排序,枚举木棍时,越长对整除拼凑的影响越大,比如数据 4 1,2,3,4 如果是从小开始找,前面可以整除,后面拼凑更不容易达到len,可以联系实际具体想一下
3.dfs判断搜到的不同情况进行不同处理,注意每次跳出一个dfs都是一次回溯,需要将回溯的前一个节点重新置空。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 70;
int sticks[maxn];
int n,len;
bool flag,vis[maxn];
int cmp(int a, int b) {
return a>b ;
}
void dfs(int deep,int now_len,int pos) { //deep为当前已经使用过的小棒数,pos为当前要处理的小棒,now_len为当前拼凑的长度
if(flag)
return;
if(now_len == 0) { //第一次或者从len的第二次新的找起,与34行情况对应,此时下一步的长度搜索对应27行,从下一根长度开始搜索
int k = 0;
while(vis[k])
k++; //已经访问的跳过
vis[k] = true;
dfs(deep+1,sticks[k],k+1);
vis[k] = false;
return;
}
if(now_len == len) { //又找到一根,判断是否找完,没找完从长度0继续找,累加
if(deep ==n) flag = true;
else dfs(deep,0,0); //如果当前已经找到了一个能整除的长度,但是拼成的根数不是总根数,那么在vis的基础之上,继续搜索
return;
}
//已经拼凑到一根或一根以上,但是没有拼凑到len的情况,此时就要枚举没有访问的树枝,开始拼凑
for(int i = pos; i < n; i++) {
if(!vis[i] && now_len + sticks[i] <= len) {
if(!vis[i-1] && sticks[i] == sticks[i-1]) continue; //重要的一步剪枝,让时间从tle到16ms的一步,后台数据有很多重复的,在递归中减少一个分支延展能节约很多时间
vis[i] = true;
dfs(deep+1,now_len+sticks[i],i+1);
vis[i] = false; //每次跳出一个递归后,就是一个回溯,相当于一个树返回上一个节点,所以要重新标记
}
}
}
int main() {
while(scanf("%d",&n)&&n){
int sum = 0;
flag = false;
for(int i = 0; i < n; i++) {
scanf("%d",&sticks[i]);
sum += sticks[i];
}
sort(sticks,sticks+n,cmp);
for(len = sticks[0]; len < sum; len++) {
if(sum %len == 0) {
memset(vis,false,sizeof(vis));
dfs(0,0,0);//已处理的树枝数目0,当前长度0,要处理的树枝0
if(flag)
break;
}
}
printf("%d\n",len);
}
return 0;
}