题解
暴搜的思路容易想到,但是剪枝细节有很多,数据很强。
搜索思路:
a. 用dfs(left_num,left_len,bound)表示当前还需要拼left_num根木棒,当前正在拼的木棒还剩left_len长度,搜索是从大往小搜索,并且当前搜索到了bound位置。
b. 每次拼一根木棒都相当于是从大到小搜索一遍所有可用的小木棒。
剪枝的思路:
- 拼小木棒的时候从大到小枚举。
- 当前枚举的小木棒a[i]如果是拼成一根大木棒的最后一根小木棒,但是最后却没有拼成所有的大木棒,那么直接从此返回失败。因为如果之后的小木棒组合能拼成所有的大木棒,那么可以将a[i]与其中的一些做置换,得到等效局面。
- 假设某根大木棒刚要开始枚举,剩余要找长度为目标大木棒长度,但是没有拼成所有的大木棒,那么直接返回失败。因为这表明无法再继续拼成任何一根大木棒了。
4.如果当前拼接长度为a[i]的木棒发生失败,那么任何长度等于a[i]的木棒都可以直接跳过。
##代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100;
int a[maxn],vis[maxn];
int n,tot,len;
bool dfs(int left_num,int left_len,int bound){//表示剩余要拼left_num根木棒,当前再拼的剩余left_len,下一个从bound开始if(left_num == 0 && left_len == 0) return true;if(left_len == 0){return dfs(left_num-1,len,tot-1);}for(int i = bound;i >= 0;--i){if(vis[i]) continue;if(a[0] > left_len) return false;vis[i] = 1;if(dfs(left_num,left_len-a[i],i-1)) return true;vis[i] = 0;if(left_len - a[i] == 0 || left_len == len) return false;while(i && a[i-1] == a[i]) --i;}return false;
}
int main(){scanf("%d",&n);int mi = 0,sum = 0;for(int i = 0;i < n;++i) {int tmp;scanf("%d",&tmp);if(tmp > 50) continue;a[tot++] = tmp;sum += tmp;mi = max(mi,tmp);}sort(a,a+tot);for(len = mi;len <= sum / 2;++len){if(sum % len != 0) continue;memset(vis,0,sizeof(vis));if(dfs(sum/len,0,0))return 0*printf("%d\n",len);}printf("%d\n",sum);return 0;
}