mg[66]:小木棍数组
vis[66]:小木棍访问数组
ans:原始木棍长度
注意注释中的用词 @小木棍@原始木棍@还原的长度
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
int n;
int mg[66];
bool vis[66];
int sum,ans;
bool ok;
bool cmp(int a,int b) {
return a>b;
}
//当前已经使用了 kth 根小木棍
//小木棍数组下标 mg_cur
//当前还原的长度 nowlen
static void dfs(int kth,int mg_cur,int nowlen) {
//mg[] 5 5 5 2 2 2 1 1 1
//vis[] 0 0 0 0 0 0 0 0 0
if(ok) return;//找到解
if(kth==n) {//所有小木棍都成功使用了
//mg[] 5 5 5 2 2 2 1 1 1
//vis[] 1 1 1 1 1 1 1 1 1
ok=true;
return;
}
if(nowlen==ans) {
dfs(kth,0,0);//kth不变 下标从0开始 nowlen清零
return;
}
if(nowlen==0) {
int k=0;
while(vis[k]) k++;//跳过已使用了的小木棍 (解释为什么上面nowlen=ans时 下标从0开始)
//我们是从最长的小木棍的长度开始枚举 小木棍数组中所有小木棍长度都不会超过ans
vis[k]=1;//使用第k根小木棍
dfs(kth+1,k+1,mg[k]);
vis[k]=0;
return;
}
//nowlen在0和ans之间,说明还没还原完一根原始木棍 还要找
for(int i=mg_cur; i<n; i++) {
if(!vis[i]&&nowlen+mg[i]<=ans) {
//下面是花式剪枝 否则TLE
//mg[] 5 5 5 2 2 2 1 1 1
//vis[] 1 0 0 0 0 0 1 0 0
if(i>0&&!vis[i-1]&&mg[i-1]==mg[i]) continue;
vis[i]=1;//表示使用第i根木棍
dfs(kth+1,mg_cur+1,nowlen+mg[i]);
if(ok) return;
vis[i]=0;
}
}
}
int main() {
while(scanf("%d",&n)&&n) {
{
//初始化
memset(vis,0,sizeof vis);
ans=0;
sum=0;
ok=false;
}
//存放小木棍
for(int i=0; i<n; i++) {
scanf("%d",&mg[i]);
sum+=mg[i];//求总和
}
sort(mg,mg+n,cmp);//从大到小进行排序
// for(int i=0;i<n;i++) cout<<mg[i]<<' ';
for(ans=mg[0]; ans<=sum/2; ans++) { //枚举原木棍长度 不超过总和的一半 不错剪枝
if(sum%ans) continue;//原始木棍长度一定是总和的因数且不小于最长的小木棍 普通剪枝
dfs(0,0,0);
if(ok) {
printf("%d\n",ans);
break;
}
}
if(!ok) printf("%d\n",sum);
}
return 0;
}
原帖在此
https://blog.csdn.net/qq_41280600/article/details/99105347
我加了些个人的理解