小木棍 P1120(回溯剪枝)

小木棍

题目描述

P1120 小木棍 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 50 50

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

第一行是一个整数 n n n,表示小木棍的个数。
第二行有 n n n 个整数,表示各个木棍的长度 a i a_i ai

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

9
5 2 1 5 2 1 5 2 1

样例输出 #1

6

提示

对于全部测试点, 1 ≤ n ≤ 65 1 \leq n \leq 65 1n65 1 ≤ a i ≤ 50 1 \leq a_i \leq 50 1ai50

思路

回溯+剪枝。这题是不能直接计算出最小的分组长度的,只能靠枚举和判断。

优化: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;
}

你可能感兴趣的:(剪枝,深度优先,算法)