POJ 1011 Sticks 木棒

原题链接

题意:右上角Language 简体中文,拿好不谢

分析:
主要算法 STMD算法(搜索)+剪枝
详细方法 枚举ANS(Answer),搜索能否完成。能够完成直接输出结果下个Case,否则继续搜索下个ANS

此题重点在于剪枝,剪枝很重要,先给出几个剪枝方法:

1 ANS必须是SUM(所有木棍长度和)的约数,否则一定拼不成若干个ANS,这是显而易见的。

2 正在拼凑某一长度木棍时,如果发现当前尝试的小棒无法胜任,那他的亲戚们(和他一样长的棍子们)一定也不能胜任,所以我们直接略过就好。此剪枝对于"10,10,10,10,10,10,10......10,1"这种有许多重复长度小棒的数据威力十分巨大,堪比小男孩,一定要加哦。

3 对于每一个ANS,我们在尝试最好从长小棒试起,就像装箱时大件物品装起来不容易一样,越长的小棒越不容易装进去,生活经验告诉我们这样安排计算机感觉会轻松些,你的Time也就更赏心悦目一些。

4 如果我们当前尝试拼凑一个长为ANS的木棍,而且还没有放进任何一根木棍(也就是当前已经凑成的长度为0),那我们一定要把最长的没用过的那根小棒放进去(因为他早晚都要进去的),如果继续凑能拼成,那就递归下一根,否则直接return false就好。本剪枝威力也很巨大,堪比胖子。

5 在枚举ANS的时候不必从1开始,这明显不行哈(除非数据是"1,1,1,1"),所以我们应该从MaxLength(最长小棒长度)搜起,这也是显而易见的,而且毫不费力就可以实现。

6 对于ANS如果枚举到了SUM-ANS还不行,那就别试了,直接放弃吧。为啥子呢?你看,要是ANS==SUM-ANS,那ANS就是SUM/2了,明显下一个结果就是SUM/1了,那不就是SUM吗,而SUM还用试吗?不过有了1剪枝,本剪枝也显得有些鸡肋啊,不过还是可以避免那么几次递归的......

其实不一定要按照ANS枚举啦,按照拼成的木棒数枚举也是可行的,也许还能好那么一点,但老朽就不代码实现了。

代码实现

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,ans,sum,lst,fnd,len[65],usd[65],nxt[65];
inline bool sch(int lft,int lth){
    if(!lft)    return true;
    if(lth==ans)    return sch(lft-1,0);
    if(!lth)
        for(int i=n-1;i>=0;i--)
            if(!usd[i]){
                usd[i]=true;
                if(sch(lft,len[i])) return true;
                usd[i]=false;
                return false;
            }
    for(int i=n-1;i>=0;i--)
        if(!usd[i]&&len[i]<=ans-lth){
            usd[i]=true;
            if(sch(lft,lth+len[i])) return true;
            usd[i]=false,i=nxt[i];
        }
    return false;
}
int main(){
    while(scanf("%d",&n)&&n){
        sum=0,lst=0,nxt[0]=-1,fnd=0;
        for(int i=0;i<n;i++)
            scanf("%d",&len[i]),sum+=len[i];
        sort(len,len+n);
        for(int i=1;i<n;i++){
            if(len[i]!=len[lst])
                nxt[i]=lst+1,lst=i;
            else nxt[i]=lst;
        }
        for(ans=len[n-1];ans<=sum-ans;ans++)
            if(!(sum%ans)){
                memset(usd,false,sizeof(usd));
                if(sch(sum/ans,0)){
                    fnd=true;
                    break;
                }
            }
        printf("%d\n",fnd?ans:sum);
    }
    return 0;
}

嗯嗯,该代码16MS跑完,一般般啦…
有些缩写介绍一下:

  • ans->answer
  • lst->last
  • fnd->find
  • len->length
  • usd->used
  • nxt->next
  • sch->search
  • lft->left
  • lth->length

By YOUSIKI

你可能感兴趣的:(POJ 1011 Sticks 木棒)