zoukankan      html  css  js  c++  java
  • 搜索中的剪枝

    一般来说剪枝有以下几类

    (1)优化搜索顺序

    比如一些有多个物品然后凑重量的题就可以重量大的优先,根据重量排序

    (2)排除等效冗余

    这是我最容易忽略的一点。在拼木棍那题中有淋漓尽致的体现

    (3)可行性剪枝

    如果当前无论如何都无法到达递归边界就剪掉

    (4)最优性剪枝

    这个就很常见了,形如if(now >= ans) return

    因为无论如何都无法有更优的答案

    (5)记忆化

    如果会重复遍历状态,那就可以用记忆化

    几道例题

    poj 1011

    http://poj.org/problem?id=1011

    非常经典的一道题,拼木棍

    枚举答案len,用搜索去判断能不能拼成

    (1)优化搜索顺序

    长度从大到小排序

    (2)排除等效冗余

    先用a再用b和先用b再用a是一样的,所以可以规定从上一根木棍的下一根开始拼

    如果一根木棍去拼不能拼成,那么之后同样长度的木棍一样也不能,要剪去

    如果当前已经拼了的长度为0,而又无法拼成,那么剪掉,因为这个时候木棍的选择是最多的,这个时候都不行,继续下去木棍选择变少更不行

    #include<cstdio>
    #include<algorithm>
    #include<functional>
    #include<cstring>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    const int MAXN = 100 + 10;
    int a[MAXN], vis[MAXN], len, n, num;
    
    bool dfs(int cnt, int last, int now)
    {
        if(cnt == num) return true;
        if(now == len) return dfs(cnt + 1, 1, 0);
        
        int fail = 0; //剪枝  
        _for(i, last, n) //剪枝  
            if(!vis[i] && now + a[i] <= len && fail != a[i])
            {
                vis[i] = 1;
                if(dfs(cnt, i + 1, now + a[i])) return true;
                vis[i] = 0;
                fail = a[i];
                if(now == 0) return false; ////剪枝  
            }
            
        return false;
    }
    
    int main()
    {
        while(~scanf("%d", &n) && n)
        {
            int sum = 0, maxt = 0;
            _for(i, 1, n) 
            {
                scanf("%d", &a[i]);
                sum += a[i];
                maxt = max(maxt, a[i]);
            }
        
            sort(a + 1, a + n + 1, greater<int>()); //剪枝 
            for(len = maxt; len <= sum; len++) //枚举答案 
                if(sum % len == 0)
                {
                    num = sum / len;
                    memset(vis, 0, sizeof(vis));
                    if(dfs(0, 1, 0)) break;
                }
                
            printf("%d
    ", len);
        }
        
        return 0;
    }

    [NOI1999]生日蛋糕

    https://www.luogu.org/problemnew/solution/P1731

    这道题自己独立做的时候拿了70分,少想到了一个可行性剪枝

    总结一下

    (1)对于最优性剪枝,可以预处理出剩下情况下最优的答案来判断

    (2)关于可行性剪枝,可以从最大和最小来判断。剩余情况最大都小于目标状态,剪掉,

    剩余状况最小都大于,目标状态,剪掉

    #include<cstdio>
    #include<algorithm>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    int n, m, ans, v[25], g[25]; 
    
    void dfs(int cnt, int R, int H, int V, int S)
    {
        int left = m - cnt;
        if(S + g[left] >= ans) return;         //最优性剪枝
        if(V + v[left] > n) return;            //可行性剪枝           
        if(V + R * R * H * left < n) return;   //可行性剪枝,这个我漏了 
        
        if(cnt == m) 
        { 
            if(V == n) ans = S; 
            return; 
        }
        
        for(int r = R - 1; r >= left; r--)  //可行性剪枝 
            for(int h = H - 1; h >= left; h--) //可行性剪枝  
                if(V + r * r * h <= n)
                    dfs(cnt + 1, r, h, V + r * r * h, S + 2 * r * h);
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        _for(i, 1, 20) v[i] = i * i * i;
        _for(i, 1, 20) g[i] = 2 * i * i;
        
        ans = 1e9;
        for(int h = n / (m * m); h >= m; h--)
            for(int r = m; r * r * h <= n; r++)
                dfs(1, r, h, r * r * h, r * r + 2 * r * h);
        printf("%d
    ", ans == 1e9 ? 0 : ans);
        return 0;
    }
  • 相关阅读:
    A1132 Cut Integer (20分)
    A1131 Subway Map (30分)
    A1130 Infix Expression (25分)
    A1129 Recommendation System (25分)
    A1128 N Queens Puzzle (20分)
    arm指令集
    韦东山视频地址
    汇编知识
    emacs 使用教程
    ip
  • 原文地址:https://www.cnblogs.com/sugewud/p/9894038.html
Copyright © 2011-2022 走看看