题目大意:给出不多于50个的木棍块,这个木棍块又是由一些等长的木棍被随意切割而成的,所以现在题目要求要找出这些木棍块能拼出的最小的木棍长度。
解题思路:这题就是dfs + 剪枝。
剪枝的地方有4处:
1.木棍的长度一定会是这些木棍块加起来的长度的约数,并且木棍是被切割的,所以一定不能小于最长木棍块长。
2.将木棍块从大到小排序,找能否拼成某个长度木棍的木棍块先从最长的开始,这样的话能过比较快的判断出这个木棍的长度是否能够被拼出。
3.如果在同一个状态下,就是在同一个递归,同一个for()循环中,发现s[i]这个长度的木棍块没办法和后面的木棍块拼出你想要的木棍的话,那么如果s[i - 1]和s[i]相等的话就可以直接不判断这个木棍块了。
4.如果是在刚开始选木棍块来拼木棍的话(可以是拼完一条了,准备下一条开始的时候),发现当前选的这个和别的都拼不上,那么最后的话这个木棍块一定会剩下,所以这种情况就可以直接返回,节约时间。
注意这里如果拼起来大于你所要的木棍长度的话还得继续判断,并不能直接都否决,因为可能是和这几个木棍块组不成,和别的就可以组成了也说不定。唯独是碰到上面情况4的时候,所以这里的dfs就要从是否==你要的长度和是否< 你要的长度的两种情况来写。具体看代码吧。还有这题有个坑(别人和我说的)数组开55竟然过不了,得开105的说,不理解。。。
#include<string.h>
#include<stdio.h>
#include<algorithm>
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;
}