小木棍

题目传送门

这题有四个剪枝:

  1. 优化搜索顺序,将木棍长按照从大到小排序
  2. 枚举木棍时保证编号递增。
  3. 剪掉冗余搜索状态,同一组内的重复元素直接跳过
  4. 如果这根木棍是这一组的第一根或最后一根,搜索完直接返回。
    结论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;
}
posted @ 2023-04-29 09:24  wscqwq  阅读(15)  评论(0编辑  收藏  举报