小木棍
题目传送门
这题有四个剪枝:
- 优化搜索顺序,将木棍长按照从大到小排序
- 枚举木棍时保证编号递增。
- 剪掉冗余搜索状态,同一组内的重复元素直接跳过
- 如果这根木棍是这一组的第一根或最后一根,搜索完直接返回。
结论4的证明:如果当前木棍是第一根,下面的没有使用过的木棍都是等效的,如果这里不行,那么下面的一定也不行。如果是最后一根,用一根木棍一定比用几根更短的木棍要更灵活一些。
然后洛谷上的最后一个数据点很恶心,需要卡常。
#include<bits/stdc++.h>
using namespace std;
#define L(i,l,r) for(int i=l;i<=r;++i)
#define R(i,l,r) for(int i=r;i>=l;--i)
const int N=70;
int n,a[N],len,sum,tot;
bool flag,st[N];
void dfs(int dep,int now,int lst){
if(flag)return;
if(now==len){dfs(dep+1,0,1);return;}
if(dep==tot){
flag=1;
return;
}
int last=-1;
L(i, lst, n){
if(st[i]||a[i]==last)continue;
last=a[i];
if(now+a[i]<=len){
st[i]=1;
dfs(dep,now+a[i],i+1);
st[i]=0;
if(!now||now+a[i]==len)return;
}
}
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&n);
memset(st,0,sizeof st);
flag=len=sum=0;
L(i, 1, n)scanf("%d",a+i),len=max(len,a[i]),sum+=a[i];
sort(a+1,a+1+n,greater<int>());
// cout<<len<<' '<<sum/2<<endl;
L(i, len, sum/2)
if(sum%i==0){
// printf("%d\n",i);
len=i;
tot=sum/i;
dfs(1,0,1);
if(flag){
printf("%d\n",i);
break;
}
}
if(!flag)printf("%d\n",sum);
return 0;
}