P1120 小木棍 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 50 50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
第一行是一个整数 n n n,表示小木棍的个数。
第二行有 n n n 个整数,表示各个木棍的长度 a i a_i ai。
输出一行一个整数表示答案。
9
5 2 1 5 2 1 5 2 1
6
对于全部测试点, 1 ≤ n ≤ 65 1 \leq n \leq 65 1≤n≤65, 1 ≤ a i ≤ 50 1 \leq a_i \leq 50 1≤ai≤50。
回溯+剪枝。这题是不能直接计算出最小的分组长度的,只能靠枚举和判断。
优化:1,排序从大到小,小的更加灵活,放到后面凑会更好;大的放在前面可以减少dfs次数。2,分组要完整,最小组长为[max:ai,sum of ai]。3,重点剪枝策略(难):①回溯过程中发现存在a[i]==res那么直接return 0即可,因为用了当前的长木棍后,之后的短木棍都没能拼出目标的木棒长度(更灵活都没拼出来),如果用其他的更小木棍去等效替换这个a[i]也必不可能凑出最终结果;②len==rest的情况意味着这个木棒还没有填入木棍,且当前a[i]是最大的木棍,而且它匹配失败了,假如能成功,那么它应该存在于某个木棒的体内,但是事实上失败了,意味着它不应该作为最大的木棍存在于某个木棒体内。③,与a[i]相等的元素没必要再尝试了,因为已经出问题了。
#include
typedef long double ld;
typedef long long ll;
#define pb push_back
#define mk make_pair
#define mt make_tuple
#define eb emplace_back
#define pob pop_back
#define rz resize
#define mem(a,b) memset(a,b,sizeof(a))
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define debug(a) cout<<#a<<"="<<a<<endl;
#define xx first
#define yy second
#define qwe(i,a,b) for(int i=a;i<=b;i++)
#define ewq(i,a,b) for(int i=a;i>=b;i--)
inline ll rr(){ll f=1,x=0;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return f*x;}
using namespace std;
const int INF=0x3f3f3f3f;
const ll mod[2]={int(1e9 + 7), 10007};
const int base[2]={29,31};
const int maxn=2e2+6;
int a[maxn],sum,sta,n,vis[maxn];
int cmp(int a,int b) {
return a>b;
}
bool dfs(int res,int len,int num,int pos) {
if(res==0 && num==n) return 1;
if(res==0) res=len,pos=1;
for(int i=pos;i<=n;i++) {
if(vis[i]) continue;
if(a[i]<=res) {
vis[i]=1;
// 不能写成 return dfs(res-a[i],len,num+1);否则无法回溯
if(dfs(res-a[i],len,num+1,i+1)) return 1;
vis[i]=0;
if(a[i]==res) break;
if(len==res) break;
while (a[i]==a[i+1]) i++;
}
}
return 0;
}
int main(int argc, char const *argv[]) {
// ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
n=rr();
qwe(i,1,n) {int x=rr(); if(x<=50) a[i]=x,sum+=x;}
sort(a+1,a+n+1,cmp); // 小的比大的灵活度更高,而且这样写能减少dfs次数
sta=a[1];
bool fl=1;
for(int i=sta;i<=sum;i++) {
if(sum%i==0) { // 必需要能够完成完整的分组
if(dfs(i,i,0,1)){std::cout << i << '\n';fl=0;}
}
}
if(fl) std::cout << sum << '\n';
return 0;
}