传说中的入门级经典DFS搜索+剪枝,果然一看答案就会了。。。
因为不存在顺序问题,当不符合条件时长棍更容易检测出来,所以从大到小排列。
朴素的DFS搜索:
int dfs( int temp_sum, int remain, int num){
int i;
if( remain == 0){
if( num == 0) return 1;
else remain = temp_sum;
}
for( i = 0; i < n; i++){
if( vis[i] == 0){
if( remain >= a[i]){
vis[i] = 1;
if( dfs( temp_sum, remain-a[i], num-1)) return 1;
vis[i] = 0;
}
}
}
return 0;
}
剪枝思路:
① 原最小棍长一定是总和的因数
② 如果棍长不符合当前所有棍的和,就放弃搜索
③ 相等棍长的棍只用搜索一次
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp( const void* a, const void* b){ // 从大到小排列
return *(int*)b - *(int*)a;
}
int ans, n;
int a[100], vis[100];
int dfs( int temp_sum, int remain, int num);
int main(void){
int i, sum;
while( scanf("%d", &n)!= EOF && n){
sum = 0;
for( i = 0; i < n; i++){
scanf("%d", &a[i]);
sum += a[i]; // 在这里求所有棍长的和
}
qsort( a, n, sizeof(int), cmp);
memset( vis, 0, sizeof(vis)); // 把访问数组设置为0
for( ans = 2; ans < sum; ans++){
if( sum % ans) continue; // 剪枝1:原最小棍长一定是总和的因数
else if( dfs( ans, ans, n)) break;
}
printf("%d\n", ans);
}
return 0;
}
int dfs( int temp_sum, int remain, int num){ // 参数设置:暂时的棍长用来试验,余下的长度,余下的棍数
int i;
if( remain == 0){ // 满足了一次棍长
if( num == 0) return 1; // 若此时所有的木棍都被用过,则此时的棍长满足题意,返回成功
else remain = temp_sum; // 否则重置剩余棍长,继续试验
}
for( i = 0; i < n; i++){ // 从第0根木棍开始试验
if( vis[i] == 0){ // 只试验未被访问的棍
if( remain >= a[i]){ // 若此时棍长小于剩余棍长
vis[i] = 1; // 假设使用
if( dfs( temp_sum, remain-a[i], num-1)) return 1; // 继续向下搜索,若满足条件停止搜索
vis[i] = 0; // 所有搜索都不满足条件,则此木棍不在当前组合中
if( a[i] == remain || remain == temp_sum) break; // 剪枝2:若没有符合条件的木棍,说明当前棍长不符合条件,认为失败,停止搜索
while( a[i] == a[i+1]) i++; // 剪枝3:如果随后的木棍和此木棍相同长度,则跳过不搜索
}
}
}
return 0; // 返回失败
}