深搜回溯剪枝——小 木 棍!

导语

众所周知,深度优先搜索能帮助我们解决很多棘手的问题。

其中,回溯、剪枝算法作为深搜算法的姊妹,其地位显然是很高滴。

今天的这篇文章(题解)可以帮自己复习“回溯”算法和 小木棍 这道究极回溯剪枝题!

原题 

(Source:CERC 1995)

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

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

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

输入格式

第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。

第二行为 N 个用空格隔开的正整数,表示 N 根小木棍的长度。

输出格式

输出仅一行,表示要求的原始木棍的最小可能长度。

标签

深度优先搜索(dfs) 剪枝  回溯  恶心人(bushi)

分析

首先,这道题考虑用暴力算法去打,结果可想而知——T L E(Time Left Enough)

好吧,这道题的恶心之处已经显露出来了,所以我们正式想到——剪枝大法,回溯大法!

 

os:呃呃,好像,,不知道,,,从哪考虑丫。。。。

 

那就一步一步来!

第一步,想到这么多木棍杂乱无章,既然是复原木棍,那么复原回去的长度应该是有限度的。

1、最后的长度(答案)一定比这堆木棍中的最长的木棍要长(或者相等)

不然的话最长的那根木棍就没有办法拼了!

2、最后的长度一定小于等于所有短木棍的总长度

否则就永远不可能复原了

 

(所以要排序!)

 

第二步,明白一个事实:

短的木棍一定比长的木棍更有利用空间!

这个不用解释,很好理解。

 

(所以要从大到小排!)

 

第三步,为了更加省时间,再仔细观察

只有被现在的木棍总长度整除的数字才能有可能成为最终的答案!

而且为了更加更加节省时间,最后一次(总和/2)可以直接判定!(这个真的想不到)

 

然后,一个字

 

搜!

可是,正当你搜索时,ErrorErrorError...

你:......

好吧,还是不行....

 

开一个数组 used [ i ]表示已经搜过的木棍

开一个二分来快速找到要找的木棍

dfs中一旦找到答案立即return

 

好不容易加上这三条...

终于......

A C 啦 !

 

The Whole Code

#include<bits/stdc++.h>
#define int long long
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
using namespace std;
int n;
const int MAXN=70;
int a[MAXN];
bool cmp(int a,int b) {
    return a>b;
}
int cnt;
int sum;
int Next[MAXN];
int Count;
bool used[MAXN];
int len;
bool flag;
void dfs(int id,int last, int remain) {
    int i;
    if(remain==0) {
        if(id==Count) {
            flag=true;
            return;
        }//find all and find
        for(i=1; i<=cnt; i++) {
            if(used[i]==0) {
                break;
            }
        }
        used[i]=1;
        dfs(id+1,i,len-a[i]);
        used[i]=0;
        if(flag) {
            return;
        }//find find find
    }//success
    int Left=1+last;
    int Right=cnt;
    int mid;
    while(Left<Right) {
        mid=(Left+Right)>>1;
        if(a[mid]<=remain) {
            Right=mid;
        } else {
            Left=mid+1;
        }
    }//find the Next(Left>=remain)
    for(i=Left; i<=cnt; i++) {
        if(used[i]==0) {
            used[i]=1;
            dfs(id,i,remain-a[i]);//search
            used[i]=0;//return
            if(flag==true) {
                return;
            }//last find and exit
            if(remain==a[i]||remain==len) {
                return;
            }
            i=Next[i];//find the Next mugun
            if(i==cnt) {
                return;
            }//find all but not find
        }
    }
}
signed main() {
    n=read();
    int lin;
    for(int i=1; i<=n; i++) {
        lin=read();
        if(lin>50) {
            continue;
        } else {
            a[++cnt]=lin;
            sum+=lin;
        }
    }
    sort(a+1,a+1+cnt,cmp);
    Next[cnt]=cnt;
    for(int i=cnt-1; i>=1; i--) {
        if(a[i]==a[i+1]) {
            Next[i]=Next[i+1];
        } else {
            Next[i]=i;
        }
    }
    for(len=a[1]; len<=sum/2; len++) {
        if(sum%len!=0) {
            continue;
        }
        Count=sum/len;
        flag=false;
        used[1]=1;
        dfs(1,1,len-a[1]);
        used[1]=0;
        if(flag) {
            cout<<len;
            return 0;
        }
    }
    cout<<sum;
    return 0;
}

 

最后

写文不易,点赞再走嘛~~

 Update

2022/4/5 标题删改

posted @ 2022-03-12 20:19  JX_weak  阅读(168)  评论(0)    收藏  举报