zoukankan      html  css  js  c++  java
  • 搜索_DFS

    连通性

    1112. 迷宫

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    const int N = 110;
    char g[N][N];
    int n;
    int sx, sy, ex, ey; 
    int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
    bool st[N][N];
    
    bool dfs(int x, int y) {
        if(x == ex && y == ey) {
            return true;
        }
        
        if(g[x][y] == '#') return false;
    
        
        st[x][y] = true;
        
        for(int i = 0; i < 4; i++) {
            int xx = x + dx[i], yy = y + dy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= n) continue;
            if(st[xx][yy]) continue ;
            if(dfs(xx, yy)) return true;
        }
        
        return false;
    }
    
    int main() {
        int T; 
        scanf("%d", &T);
        while(T--) {
            memset(st, false, sizeof st);
            scanf("%d", &n);
            for(int i = 0; i < n; i++) cin >> g[i];
            cin >> sx >> sy >> ex >> ey;
            
            if(g[sx][sy] == '#' || g[ex][ey] == '#') puts("NO");
            else if(dfs(sx, sy))
                puts("YES");
            else 
                puts("NO");
    
        }
        return 0;
    }
    

    搜索顺序

    • 需要恢复现场的原因:联想二叉搜索树,保证当前节点对于每一个儿子节点的状态都是相同的

    指数型枚举: 无个数限制

    • 题目描述: 从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
    1. 朴素dfs枚举
    int n;
    vector<int> ans;
    void calc(int x) {
        if(x == n + 1) {
            for(auto c: ans) printf("%d ", c);
            puts("");
            return ;
        }
        calc(x + 1);
        
        ans.push_back(x);
        calc(x + 1);
        ans.pop_back(); // 恢复原始状况
        
    }
    
    void solve() {
        scanf("%d", &n);
        calc(1);
    }
    
    1. 二进制压缩版dfs
    int n;
    void dfs(int u, int state) { // state 的二进制表示中,选过的数所对应的二进制位为1
        if(u == n) { // 从零开始到n - 1结束
            for(int i = 0; i < n; i++) {
                if(state >> i & 1) printf("%d ", i + 1); // state的二进制表示中为一的表示选中,输出
            }
            printf("
    ");
            return ;
        }
        dfs(u + 1, state); // 不选
        
        dfs(u + 1, state | 1 << u);  // 选, 并将其置为一
    
    }
    
    void solve() {
        scanf("%d", &n);
        dfs(0, 0);
    }
    

    组合型枚举:有个数限制

    • 题目描述: 从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
    1. 朴素dfs: 运行时间过长
    vector<int> ch;
    int a, b;
    void calc(int x) {
        if(x > a + 1) return ;
        if(ch.size() == b) {
            for(int i = 0; i < ch.size(); i++) printf("%d ", ch[i]);
            printf("
    ");
            return ;
        }
        ch.push_back(x);
        calc(x + 1);
        ch.pop_back();
        
        calc(x + 1);
    } 
    
    void solve() {
        scanf("%d%d", &a, &b);
        calc(1);
        return 0;
    }
    
    1. 二进制压缩版dfs
    int a, b;
    void dfs(int u, int state, int s) { 
        if(s + a - u < b) return ; // 当前加上剩下的所有的也不满足条件
        if(s == b) {
            for(int i = 0; i < a; i++) {
                if(state >> i & 1) printf("%d ", i + 1);
            }
            printf("
    ");
            return;
        }
        dfs(u + 1, state | 1 << u, s + 1); // 选
        dfs(u + 1, state, s);  // 不选
    }
    
    void solve() {
        scanf("%d%d", &a, &b);
        dfs(0, 0, 0);
    }
    

    排列型枚举: 排列数

    • 题目描述:把 1~n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
    1. 朴素版
      • 建立一个布尔数组判断数是否被选过
    const int N = 10;
    vector<int> ch;
    bool st[N];
    int n;
    
    void dfs(int u) {
        if(u == n + 1) {
            for(int i = 0; i < n; i++) printf("%d ", ch[i]);
            printf("
    ");
            return ;
        }
        for(int i = 1; i <= n; i++) {
            if(!st[i]) {
                ch.push_back(i);
                st[i] = true;
                dfs(u + 1);
                st[i] = false; // 恢复现场
                ch.pop_back(); // 恢复现场
            }
        }
    }
    
    void solve() {
        scanf("%d", &n);
        dfs(1);
        return 0;
    }
    
    1. 二进制压缩版dfs
      • 使用一个32位整型变量,使用其二进制表示数是否被使用, 为1的位表示已经使用
    const int N = 10;
    vector<int> ch;
    int n;
    
    void dfs(int u, int state) {
        if(ch.size() == n) {
            for(int i = 0; i < n; i++) printf("%d ", ch[i]);
            printf("
    ");
            return ;
        }
        for(int i = 1; i <= n; i++) {
            if(!((state >> i) & 1)) {
                ch.push_back(i);
                dfs(u + 1, state | 1 << i); // 选择该数,置为1
                ch.pop_back(); // 恢复现场
            }
        }
    }
    
    void solve() {
        scanf("%d", &n);
        dfs(1, 1);
        return 0;
    }
    

    剪枝

    165. 小猫爬山

    • 加入把重量大的猫放入缆车,那么该状态的子树的分支将会是比较少的,因此采用这种策略进行DFS
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    int w[20], sm[20];
    int ans = 20;
    int n, W; 
    
    void dfs(int u, int k) {
        // 最优化剪枝
        if(k >= ans) return ;
    
        if(u == n) {
            ans = k;
            return ;
        }
        
        for(int i = 0; i < k; i++) {
            // 可行性剪枝
            if(sm[i] + w[u] <= W) {
                sm[i] += w[u];
                dfs(u + 1, k);
                sm[i] -= w[u];
            }
        }
        
        sm[k] = w[u];
        dfs(u + 1, k + 1);
        sm[k] = 0;
    }
    
    int main() {
        scanf("%d%d", &n, &W);
        for(int i = 0; i < n; i++) scanf("%d", &w[i]);
        
        
        sort(w, w + n);
        reverse(w, w + n);
        
        dfs(0, 0);
        cout << ans;
        return 0;
    }
    

    166. 数独

    #include <iostream>
    
    using namespace std;
    const int N = 9, M = 1 << N;
    int mp[M], ones[M];
    int col[N], row[N], cell[3][3];
    char str[100];
    
    void init() {
        for(int i = 0; i < N; i++)
            col[i] = row[i] = (1 << N) - 1;
        for(int i = 0; i < 3; i++)
            for(int j = 0; j < 3; j++)
                cell[i][j] = (1 << N) - 1;
    }
    
    void draw(int x, int y, int t, bool st) {
        if(st) str[x * N + y] = '1' + t;
        else str[x * N + y] = '.';
        
        int v = 1 << t;
        if(!st) v = -v;
        row[x] -= v;
        col[y] -= v;
        cell[x / 3][y / 3] -= v;
        
    }
    
    // 返回最后一个1
    int lowbit(int x) {
        return x & -x;
    }
    
    int get(int x, int y) {
        return row[x] & col[y] & cell[x / 3][y / 3];
    }
    
    bool dfs(int cnt) {
        if(!cnt) return true;
        int minv = 10;
        int x, y;
        for(int i = 0; i < N; i++)
            for(int j = 0; j < N; j++)
                if(str[i * N + j] == '.') {
                    int state = get(i, j);
                    if(ones[state] < minv) {
                        minv = ones[state];
                        x = i, y = j;
                    }
                }
        // cout << x << ' ' << y << endl;
        int state = get(x, y);
        for(int i = state; i; i -= lowbit(i)) {
            int t = mp[lowbit(i)];
            draw(x, y, t, true);
            if(dfs(cnt - 1)) return true;
            draw(x, y, t, false);
        }
    
        return false;
    }
    
    int main() {
        for(int i = 0; i < 1 << N; i++) 
            for(int j = 0; j < N; j++)
                ones[i] += i >> j & 1;
        for(int i = 0; i < N; i++) mp[1 << i] = i;
    
        
        while(cin >> str, str[0] != 'e') {
            init();
            int cnt = 0;
            for(int i = 0, k = 0; i < N; i++)
                for(int j = 0; j < N; j++, k++)
                    if(str[k] != '.') {
                        int t = str[k] - '1';
                        draw(i, j, t, true);
                    } else 
                        cnt++;
            
            dfs(cnt);
            puts(str);
        }
        return 0;
    }
    

    167. 木棒

    • 剪枝1:所有木棍的长度之和必须是组数的倍数
    • 剪枝2:从大到小枚举木棒
    • 剪枝3:按照组合数方式枚举
    • 剪枝4:如果当前木棒不能加到其中,那么可以直接略过后面所有与此木棒长度相等的木棒
    • 剪枝5:如果是木棒的第一根失败,那么接下来的一定失败
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    const int N = 70;
    int a[N], n, tot, length;
    int ans;
    bool st[N];  // 剪枝3
    
    // 当前枚举的大棍数量,当前长度,开始位置
    bool dfs(int u, int s, int start) {
    
        if(u * length == tot) return true;
    
        if(s == length) return dfs(u + 1, 0, 0);
        
        for(int i = start; i < n; i++) {
            if(st[i] || s + a[i] > length) continue;
            st[i] = true;
            if(dfs(u, s + a[i], i + 1)) return true;
            st[i] = false;
            
            // 剪枝5、1
            if(!s || s + a[i] == length) return false;
    
            // 剪枝4
            int j = i;
            while(j < n && a[i] == a[j]) j++;
            i = j - 1;
        }
        return false;
    }
    
    
    int main() {
        while(cin >> n, n) {
            tot = 0;
            memset(st, false, sizeof st);
            for(int i = 0; i < n; i++) {
                scanf("%d", &a[i]);
                tot += a[i];
            }
            // 剪枝2
            sort(a, a + n); reverse(a, a + n);
            length = 1;
            while(true) {
                if(tot % length == 0 && dfs(0, 0, 0)) {
                    cout << length << endl;
                    break;
                }
                else length++;
            }
        }
        
        return 0;
    }
    

    168. 生日蛋糕

    • 剪枝1:从底向上搜索,从大到小枚举(r、h)

    • 剪枝2:当前第(u)层,每层半径高度均严格递增

      1. (R_u leq min(R_{u + 1} - 1, sqrt{N - V}))
        • (R_u leq R_{u + 1} - 1)
        • (u)层之下的总体积为(V), 总体积为(N), 从(u)层之上的总体积为:(N - V = pi R^2H),题目不用考虑(pi), (H) 最小取(1), 因此(R_u leq sqrt{N - V})
      2. (H_u leq min(H_{u + 1}, frac{N - V}{R^2}))
    • 剪枝3:预处理前(u)层体积最小值(minv)、表面积最小值(mins),以保证:

      • (V + minv_u leq N)
      • (S + mins_u < S)
    • 剪枝4:(s + frac{2(N - V)}{R_(u + 1)} + 1 geq ans), break;

      • (S_u = sum_{k = 1}^u2 pi R_kH_k)
      • (N - V = sum_{k = 1}^upi R_k^2H_k)
      • 忽略(pi): (S_u = frac{2}{R_u + 1}sum_{k = 1}^u R_kHR_{u + 1} > frac{2}{R_u + 1}sum_{k = 1}^upi R_k^2H_k)
      • (S_{u} + s geq frac{2(N - V)}{R_(u + 1)})

    迭代加深

    170. 加成序列

    • 题目特点:对于有些分支可能搜索深度很深,但最终答案只在深度很浅的分支当中
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    const int N = 110;
    int n, path[N];
    
    bool dfs(int u, int depth) {
        if(u > depth) return false;
        if(path[u - 1] == n) return true;
        bool st[N] = {false};
        for(int i = u - 1; i >= 0; i--) 
            for(int j = i; j >= 0; j--) {
                int s = path[i] + path[j];
                if(s > n || s <= path[u - 1] || st[s]) continue;
                st[s] = true;
                path[u] = s;
                if(dfs(u + 1, depth)) return true;
            }
                
        return false;
    }
    
    int main() {
        path[0] = 1;
        while(cin >> n, n) {
            int depth = 1;
            while(!dfs(1, depth)) depth ++;
            for(int i = 0; i < depth; i++) 
                cout << path[i] << " ";
            cout << endl;
        }
        return 0;
    }
    

    双向DFS

    171. 送礼物

    IDA*

  • 相关阅读:
    [LeetCode] 336. Palindrome Pairs
    [LeetCode] 214. Shortest Palindrome
    [LeetCode] 844. Backspace String Compare
    [LeetCode] 5. Longest Palindromic Substring
    [LeetCode] 269. Alien Dictionary
    [LeetCode] 200. Number of Islands
    [LeetCode] 72. Edit Distance
    [LeetCode] 460. LFU Cache
    [LeetCode] 1229. Meeting Scheduler
    [LeetCode] 22. Generate Parentheses
  • 原文地址:https://www.cnblogs.com/Hot-machine/p/13426730.html
Copyright © 2011-2022 走看看