zoukankan      html  css  js  c++  java
  • 深搜+剪枝--poj1011--拯救少林神棍

    题目描述:

    少林寺的宝贝“少林神棍”断成了n根长短不一的小木棒,现在要把这些小木棒重新拼若干跟等长的棍子,求棍长最短是多少?

    原题地址

    先放上从这道题应该学到的经验:

    1,要选择简合适的搜索顺序,如果一个人物分成多步,要优先尝试可能性少的。  (优先尝试长的木棒)

    2,要发现表面上的不同,实质上等效的重复状态,避免重复。

    3,根据实际问题,发现剪枝方案。

    解题思路:

    1,假设了一根棍子的长度的情况下,如何尝试去拼成若干根该长度的棍子?

    2,本地的“状态是什么”?

    3,初始状态和终止状态是什么?

    4,状态如何转移?

    具体不写了,去看看b站北大《算法基础》吧,讲的贼鸡儿好

    ok,思考出上面几点之后,都不难写出dfs实现的代码:

    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int inf=0x3f3f3f3f;
    
    vector<int> q;       //用普通数组貌似也没有不行 
    int n; 
    int L;
    
    int used[60];           //第i根有没有用过 
    
    int dfs(int r,int m){
        if(r==0 && m==0)
        return 1;
        if(m==0)
        m=L;
        for(int i=0;i<n;i++){
            if(used[i]) continue;
            if(q[i]<=m){
                used[i]=1;
                if(dfs(r-1,m-q[i]))
                return 1;
                else
                used[i]=0;
            }
        }
        return 0;
    }
    
    int main()
    {    
        freopen("in.txt","r",stdin); 
        int mark;
        while(1){
            mark=0;
            int sl=0;           //输入的总长 
            cin>>n;
            if(n==0) break;
            memset(used,0,sizeof(used));
            q.clear();
            for(int i=0;i<n;i++){
                int t;
                cin>>t;
                sl+=t;
                q.push_back(t);
            }
            sort(q.begin(),q.end(),greater<int>());       //从大到小排序 
    //        cout<<q.front();
            for(L=q.front();L<=sl/2;L++){
                if(sl%L!=0) continue;
                if(dfs(n,L)){
                mark=1;
                cout<<L<<endl;    
                }
    //            cout<<"loved";
            }
            if(!mark)
            cout<<1<<endl;
        }
        return 0; 
    }
    View Code

    是的,这个代码通不过,理由是太慢了,这就引出了这节课的重点:剪枝

    第一种剪枝方案:不要在同一位置多次尝试相同长度的木棒。

    比如说有3根长度为52的木棒,如果第一次用长度52的木棒得不到答案,那么再遇到其他长度52的木棒就直接跳过就好了

    第二种剪枝方案:如果拼某根棍子时,第一根木棒就拼不成,那就不要再换别的木棒尝试做第一根,直接拆前面(return上一层)的吧。因为,前面那个木棒你把它换掉,他一定会出现在后面的棍子里,后面的棍子也一样拼不成。

    第三种剪枝方案:如果你后面的拼不成,希望把前面的某一根棍子的最后一根木棒拿掉,去拼后面的棍子,这种方法是行不通的,原因简单。

    第四种剪枝方案:拼一根棍子,尽量里面的木棒是从长到短,因为可能一开始拼的1,3,2发现拼不成,拆掉2后拆掉3,因为按若出现前面的情况,按顺序2是在3后,3放1后面不行,下一步程序就会把2放一后面,就成了1,2,3显然这样子是没有意义的。

    ac代码:

    (明明四剪枝全加了还是不过,你猜怎么着?used数组开的不够。。。题目64我开的60,真悲哀)

    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int inf=0x3f3f3f3f;
    
    vector<int> q;       //用普通数组貌似也没有不行 
    int n; 
    int L;
    int jz4=0;            //记录最近一次放上棍子的木棒下标 
    
    int used[70];           //第i根有没有用过 
    
    int dfs(int r,int m){
        if(r==0 && m==0)
        return 1;
        if(m==0)
        m=L;
        
        int start=0;                  //剪枝4 
        if(m!=L) start=jz4+1;         //剪枝4 
        
        for(int i=start;i<n;i++){
            if(!used[i] && q[i]<=m){
                if(i>0){if(q[i]==q[i-1] && used[i-1]==0) continue;}     //剪枝一:相邻的相同长度的几根木棒,第一根不行,以后的也不行 
                
                used[i]=1;
                jz4=i;                 //剪枝4 
                if(dfs(r-1,m-q[i]))
                return 1;
                else{
                used[i]=0;
                if( q[i]==m || m==L) return 0;        //剪枝三:后面的拼不出,要拆前面的最后一棍,这是无效的,原因简单。剪枝二:如果第一根木棒就是不行的,那就换下一个数。        
                }                                     //因为它在这儿不行,以后他注定还是要出现在某一根棍子最前面,就还是不行    
            }
        }
        return 0;
    }
    
    int main()
    {    
        int mark;
        while(1){
            mark=0;
            int sl=0;           //输入的总长 
            cin>>n;
            if(n==0) break;
            memset(used,0,sizeof(used));
            q.clear();
            for(int i=0;i<n;i++){
                int t;
                cin>>t;
                sl+=t;
                q.push_back(t);
            }
            sort(q.begin(),q.end(),greater<int>());       //从大到小排序 
    //        cout<<q.front();
            for(L=q.front();L<=sl/2;L++){
                if(sl%L!=0) continue;
                if(dfs(n,L)){
                mark=1;
                cout<<L<<endl;    
                }
    //            cout<<"loved";
            }
            if(!mark)
            cout<<sl<<endl;
        }
        return 0; 
    }
    柳暗花明又一村
  • 相关阅读:
    记下mongoose(转载)
    vue vue-cli中引入全局less变量的方式
    单标签不支持 伪元素
    删除tppabs,href="javascript:if(confirm)...",、/*tpa=http://...
    系统字体放大导致rem布局错乱,解决方案,已通过测试
    IE条件注释
    hbase部署经验与坑总结
    ubuntu安装mysql 5.7
    静态代理和动态代理
    单例模式
  • 原文地址:https://www.cnblogs.com/ucandoit/p/8525028.html
Copyright © 2011-2022 走看看