洛谷P1120小木棒 爆搜+剪枝

题解

暴搜的思路容易想到,但是剪枝细节有很多,数据很强。
搜索思路:
a. 用dfs(left_num,left_len,bound)表示当前还需要拼left_num根木棒,当前正在拼的木棒还剩left_len长度,搜索是从大往小搜索,并且当前搜索到了bound位置。
b. 每次拼一根木棒都相当于是从大到小搜索一遍所有可用的小木棒。
剪枝的思路:

  1. 拼小木棒的时候从大到小枚举。
  2. 当前枚举的小木棒a[i]如果是拼成一根大木棒的最后一根小木棒,但是最后却没有拼成所有的大木棒,那么直接从此返回失败。因为如果之后的小木棒组合能拼成所有的大木棒,那么可以将a[i]与其中的一些做置换,得到等效局面。
  3. 假设某根大木棒刚要开始枚举,剩余要找长度为目标大木棒长度,但是没有拼成所有的大木棒,那么直接返回失败。因为这表明无法再继续拼成任何一根大木棒了。
    4.如果当前拼接长度为a[i]的木棒发生失败,那么任何长度等于a[i]的木棒都可以直接跳过。

##代码

#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 100;
int a[maxn],vis[maxn];
int n,tot,len;
bool dfs(int left_num,int left_len,int bound){
    //表示剩余要拼left_num根木棒,当前再拼的剩余left_len,下一个从bound开始
    if(left_num == 0 && left_len == 0) 
        return true;
    if(left_len == 0){
        return dfs(left_num-1,len,tot-1);
    }
    for(int i = bound;i >= 0;--i){
        if(vis[i]) continue;
        if(a[0] > left_len) return false;
        vis[i] = 1;
        if(dfs(left_num,left_len-a[i],i-1)) return true;
        vis[i] = 0;
        if(left_len - a[i] == 0 || left_len == len) return false;
        while(i && a[i-1] == a[i]) --i;
    }
    return false;
}
int main(){
    scanf("%d",&n);
    int mi = 0,sum = 0;
    for(int i = 0;i < n;++i) {
        int tmp;scanf("%d",&tmp);
        if(tmp > 50) continue;
        a[tot++] = tmp;
        sum += tmp;
        mi = max(mi,tmp);
    }
    sort(a,a+tot);
    for(len = mi;len <= sum / 2;++len){
        if(sum % len != 0) continue;
        memset(vis,0,sizeof(vis));
        if(dfs(sum/len,0,0))
            return 0*printf("%d\n",len);
    }
    printf("%d\n",sum);
    return 0;
}

你可能感兴趣的:(ACM-ICPC训练题解,搜索剪枝系列)