hdu 1455 : http://acm.hdu.edu.cn/showproblem.php?pid=1455
这道题是大一学习dfs时学习的,但是发现自己现在还是不会写,写着写着超时了。主要是两个剪枝想不到了,代码中给注释了。网上也有好多这道题的做法,我不多说了。
用的是没有返回值的dfs。
剪枝1:答案必须是sum的约数。
剪枝2:从大到小排序减小递归深度。
剪枝3:例如某个组合用到4,原序列中有4也有3,1,那么优先使用4,因为3,1的用处更大,就是代码中的break剪枝。
剪枝4:最重要的剪枝,第一个数一定要能够组成。
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int sticks[65];
bool vis[65];
int n, sum;
bool flag = false;
int cmp(const int a, const int b) {
return a > b;
}
// 最怕不写dfs参数注释的人了。。。。
// pos寻找可用木棍的开始位置,avg原始木棍的长度,
// cnt表示已经凑得的原木棍数,add正在组合的木棍长度
void dfs(int pos, int avg, int cnt, int add) {
if(cnt == sum / avg || flag == 1) {
flag = 1;
return ;
}
for (int i = pos; i < n; i ++) {
if(!vis[i]) {
if(add + sticks[i] == avg) {
vis[i] = 1;
dfs(0, avg, cnt + 1, 0);
vis[i] = 0;
break; // 如果大的数能够使用,小的数也能使用,优先使用大的数。
} else if(add + sticks[i] < avg) {
if(i > 0 && !vis[i-1] && sticks[i] == sticks[i-1]) {
continue;
}
vis[i] = 1;
dfs(i + 1, avg, cnt, add + sticks[i]);
vis[i] = 0;
}
// 第一根木棍一定能够组成一组,这个剪枝从TLE到0ms
if(pos == 0) {
return ;
}
}
}
}
int main() {
while (~scanf("%d", &n), n) {
sum = 0, flag = false;
for (int i = 0; i < n; i ++) {
scanf("%d", sticks + i);
sum += sticks[i];
}
sort(sticks, sticks + n, cmp);
for (int i = sticks[0]; i <= sum / 2; i ++) {
if(sum % i == 0) {
memset(vis, 0, sizeof(vis));
dfs(0, i, 0, 0);
if(flag == 1) {
printf("%d\n", i);
break;
}
}
}
if(flag == 0) {
printf("%d\n", sum);
}
}
return 0;
}