zoukankan      html  css  js  c++  java
  • 双向bfs和双向dfs

    双向bfs和双向dfs

    1. 算法分析

    当进行的变换是可逆的时候,且规定步数的上限时,可以使用双向dfs或双向bfs从源点和终点一起搜索。这样可以把时间从O(n)->O(n/2)
    如果dfs调栈超过1e5时,那么考虑双向bfs

    写法技巧

    1. 双向dfs
      第一个dfs先搜索前一半的空间,打表存储所有可达的值
      第二个dfs搜索后一半的空间,然后查询是否在前一半空间中出现过

    2. 双向bfs
      维护两个队列,当两个队列均非空时才能继续进行循环。循环内不断对元素较少的那个队列进行bfs操作

    2. 例题

    2.1 双向dfs

    acwing171 送礼物
    达达帮翰翰给女生送礼物,翰翰一共准备了N个礼物,其中第i个礼物的重量是G[i]。
    达达的力气很大,他一次可以搬动重量之和不超过W的任意多个物品。
    达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。
    N~46, W,G[i]~int

    /*
    第一个dfs打表记录前n/2的所有可能和
    第二个dfs找到小于等于w-sum的最大的那个
    */
    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 47;
    int a[N], cnt = 1, weight[1 << 25], w, n, k, ans;
    
    void dfs1(int u, int sum) {
        if (u == k) {
            weight[cnt++] = sum;
            return;
        }
        
        dfs1(u + 1, sum);  // 不选第u个
        if (sum + 0ll + a[u] <= w) dfs1(u + 1, sum + a[u]);  // 选第u个
    }
    
    void dfs2(int u, int sum) {
        
        // 当找完后n/2个后,二分查找小于等于w-s的最大值
        if (u >= n) {
            int l = 0, r = cnt - 1;
            while (l < r) {
                int mid = (l + r + 1) / 2;
                if (sum + 0ll + weight[mid] <= w) l = mid;
                else r = mid - 1;
            }
            ans = max(ans, sum + weight[l]);
            return;
        }
        
        dfs2(u + 1, sum);  // 不选第u个
        if (sum + 0ll + a[u] <= w) dfs2(u + 1, sum + a[u]);  // 选第u个
    }
    
    int main() {
        cin >> w >> n;
        int num = 0;
        for (int i = 0, t; i < n; ++i) {
            cin >> t;
            if (t <= w) a[num++] = t;
        }
        n = num;
        k = n / 2;
        
        // 优先大的先搜索
        sort(a, a + n);
        reverse(a, a + n);
        dfs1(0, 0);
        
        // 去重
        sort(weight, weight + cnt);
        cnt = unique(weight, weight + cnt) - weight;
        
        // 第二个dfs
        dfs2(k, 0);
        cout << ans;
        return 0;
    }
    

    2.2 双向bfs

    acwing190字串变换
    已知有两个字串 A, B 及一组字串变换的规则(至多6个规则):
    A1 -> B1
    A2 -> B2

    规则的含义为:在 A 中的子串 A1 可以变换为 B1、A2 可以变换为 B2 …。
    例如:A=’abcd’ B=’xyz’
    变换规则为:
    ‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’
    则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
    ‘abcd’->‘xud’->‘xy’->‘xyz’
    共进行了三次变换,使得 A 变换为B。
    若在 10 步(包含 10步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出”NO ANSWER!”

    // 设置两个队列,两个队列都非空的时候才能进行搜索
    // 如果a队列元素比b队列少,那么处理a队列;否则处理b队列
    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 6;
    string a[N], b[N];
    unordered_map<string, int> da, db;
    string S, E;
    int n;
    queue<string> qa, qb;
    
    // 进行从a->b扩展
    int extend(queue<string> &q, unordered_map<string, int> &da, unordered_map<string, int> &db, string a[], string b[]) {
        auto t = q.front();
        q.pop();
        
        for (int i = 0; i < t.size(); ++i) {  // 遍历字符串每一位
            for (int j = 0; j < n; ++j) {  // 枚举每一种变换
                if (t.substr(i, a[j].size()) != a[j]) continue;  // 如果不能替换,continue
                string new_str = t.substr(0, i) + b[j] + t.substr(i + a[j].size());  // 生成新的字符串
                if (da.count(new_str)) continue;  // 如果先前走过
                if (db.count(new_str)) return db[new_str] + da[t] + 1;  // 如果找到
                da[new_str] = da[t] + 1;  // 记录
                q.push(new_str);
            }
        }
        
        return 11;
    }
    
    int bfs() {  // bfs判断能否到达
        qa.push(S), qb.push(E);
        da[S] = 0, db[E] = 0;
        
        while (qa.size() && qb.size()) {  // 当两个队列都不为空才有可能可达
            int t;  
            if (qa.size() <= qb.size()) t = extend(qa, da, db, a, b);  // a队列数目少,那么扩展a队列使之变多
            else t = extend(qb, db, da, b, a);  // 否则扩展b队列
            
            if (t <= 10) return t;  // 如果步数小于等于10,那么找到了
        }
        return 11;
    }
    
    int main() {
        cin >> S >> E;  // 起始状态和终止状态
        while (cin >> a[n] >> b[n]) n++;  // 读入变换
        
        int ans = bfs();  // 如果步数大于10,就是无法到达
        if (ans > 10) cout << "NO ANSWER!";
        else cout << ans;
        return 0;
    }
    
  • 相关阅读:
    PM对功能模块的说明
    第四个迭代目标
    第三个迭代任务
    第三个迭代目标
    记账本状态图
    记账本数据流图
    记账本ER图
    记账本用例图
    第二个迭代目标
    记账本时序图
  • 原文地址:https://www.cnblogs.com/spciay/p/13383119.html
Copyright © 2011-2022 走看看