zoukankan      html  css  js  c++  java
  • [一本通学习笔记] 深度优先搜索与剪枝

    深度优先搜索的剪枝优化还是很灵活的。但常规来说,比较通用的优化思路主要有两类。

    • 可行性剪枝
    • 最优性剪枝

    需要结合题目性质进行一定的理解与探究。必要时还可以加入一些启发式的优化。

    一本通上的几个例题和练习做得有点卡,代码也很丑陋。

    #10018. 「一本通 1.3 例 1」数的划分

    没怎么动脑子就直接dp了

    #include <bits/stdc++.h>
    using namespace std;
    
    int n,k;
    int f[205][9][205]; //?分成?个数 最大不超过?
    
    int main() {
        cin>>n>>k;
        memset(f,0,sizeof f);
        f[0][0][1]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=k;j++)
                for(int l=1;l<=i;l++)
                    for(int t=1;t<=l;t++)
                        f[i][j][l]+=f[i-l][j-1][t];
        int ans = 0;
        for(int i=1;i<=n;i++) ans += f[n][k][i];
        cout<<ans<<endl;
    }

    #10019. 「一本通 1.3 例 2」生日蛋糕

    加入最优性剪枝后,搜索时的枚举顺序就会对时间效率产生影响

    // R[i]>R[i+1], H[i]>H[i-1]
    // Sigma(R^2*H) = V
    // Sigma(2RH) + Rm^2 = S
    #include <bits/stdc++.h>
    using namespace std;
    
    const int inf = 0x3f3f3f3f;
    int n, m, ans = inf;
    void dfs(int vol, int sur, int dep, int pr, int ph) {
        if (vol < 0 || sur > ans)
            return;
        if (2 * vol / pr + sur > ans)
            return;
        if (dep == m + 1) {
            if (vol == 0)
                ans = sur;
            return;
        }
        for (int i = pr - 1; i > m - dep; --i)
            for (int j = ph - 1; j > m - dep; --j) {
                if (dep == 1)
                    sur = i * i;
                dfs(vol - i * i * j, sur + 2 * i * j, dep + 1, i, j);
            }
    }
    
    int main() {
        cin >> n >> m;
        dfs(n, 0, 1, sqrt(n), n);
        if (ans == inf)
            cout << "0" << endl;
        else
            cout << ans << endl;
        return 0;
    }

    #10020. 「一本通 1.3 例 3」小木棍

    听说有个数据点有问题?

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, a[105], u[105];
    
    bool dfs(int idx, int rem) {
        if (rem < 0)
            return false;
        if (rem == 0)
            return true;
        for (int i = idx; i >= 1; --i) {
            if (u[i])
                continue;
            u[i] = 1;
            if (dfs(i - 1, rem - a[i]))
                return true;
            u[i] = 0;
        }
        return false;
    }
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        sort(a + 1, a + n + 1);
        int sum = 0;
        for (int i = 1; i <= n; i++) sum += a[i];
        for (int i = a[1]; i <= sum; i++) {
            if (sum % i)
                continue;
            memset(u, 0, sizeof u);
            int flag = 1;
            for (int j = 1; j <= sum / i; j++) {
                if (dfs(n, i) == false) {
                    flag = 0;
                    break;
                }
            }
            if (flag) {
                cout << i << endl;
                return 0;
            }
        }
    }

    #10021. 「一本通 1.3 例 4」Addition Chains

    题目意思描述的不是特别清楚。自己写的时候剪枝边界没选好。

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, ans = 105;
    vector<int> s, a;
    int u[305];
    
    void dfs(int p) {
        if (s.size() >= 11)
            return;
        if (s.size() >= ans)
            return;
        if (p == n + 1) {
            ans = min(ans, (int)(s.size()));
            if (ans == (int)(s.size()))
                a = s;
        } else
            for (int i = p; i <= min(2 * p + 1, n); i++) {
                if (u[i] == 0)
                    continue;
                for (int j = 0; j < s.size(); j++) u[i + s[j]]++;
                u[2 * i]++;
                s.push_back(i);
                dfs(i + 1);
                s.pop_back();
                for (int j = 0; j < s.size(); j++) u[i + s[j]]--;
                u[2 * i]--;
            }
    }
    
    int main() {
        while (cin >> n) {
            ans = 105;
            s.clear();
            if (n == 0)
                return 0;
            s.push_back(1);
            u[1] = u[2] = 1;
            dfs(2);
            // cout<<ans<<endl;
            for (int i = 0; i < a.size(); i++) cout << a[i] << " ";
            cout << endl;
        }
    }

    #10022. 「一本通 1.3 练习 1」埃及分数

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
    ll flag = 0;
    
    struct frac {
        ll p, q;
        frac() { p = q = 1; }
        frac(ll x) {
            p = 1;
            q = x;
        }
        frac(ll x, ll y) {
            p = x;
            q = y;
        }
        void simp() {
            ll g = gcd(p, q);
            p /= g;
            q /= g;
        }
        void add(frac &b) {
            p = p * b.q + q * b.p;
            q *= b.q;
            simp();
        }
        void dec(frac &b) {
            p = p * b.q - q * b.p;
            q *= b.q;
            simp();
        }
    } a;
    ll mx = 0x3f3f3f3f;
    vector<ll> sta, sans;
    ll ans, D;
    
    void dfs(ll dep, ll lim, frac rem) {
        // cout<<dep<<" "<<lim<<" "<<rem.p<<"/"<<rem.q<<endl;
        if (lim >= mx)
            return;
        if (dep == 0) {
            if (rem.p == 0) {
                flag = 1;
                mx = min(mx, lim);
                if (mx == lim)
                    sans = sta;
            }
        } else if (dep == 1 && (rem.p == 1 && rem.q > lim)) {
            flag = 1;
            mx = min(mx, rem.q);
            if (mx == rem.q) {
                sans = sta;
                sans.push_back(mx);
            }
        } else {
            if (rem.p <= 0 || (rem.p > 0 && rem.q < 0))
                return;
            for (ll i = max(lim, rem.q / rem.p) + 1; i <= 100000; i++) {
                if ((dep)*rem.q <= rem.p * i)
                    break;
                sta.push_back(i);
                frac tmp(i);
                rem.dec(tmp);
                dfs(dep - 1, i, rem);
                rem.add(tmp);
                sta.pop_back();
            }
        }
    }
    
    int main() {
        cin >> a.p >> a.q;
        int g = gcd(a.p, a.q);
        a.p /= g;
        a.q /= g;
        for (ll i = 1; i <= 100; i++) {
            D = i;
            dfs(i, 1, a);
            if (flag)
                break;
        }
        if (flag) {
            for (ll i = 0; i < sans.size(); i++) cout << sans[i] << " ";
            cout << endl;
        }
    }

    #10023. 「一本通 1.3 练习 2」平板涂色

    抽象成几个点,有依赖关系的块连边。搜索枚举的时候判断应该在该点之前涂的点是否都涂过了,这样跑的比较快。如果反过来,检验所有应该在该点之后涂的点是否都被涂过,就会跑得比较慢。

    #include <bits/stdc++.h>
    using namespace std;
    
    vector<int> g[15];
    int a[15][15], n, x[2], y[2], c[15], s[15], u[15], cnt, ans = 0x3f3f3f3f;
    int _c = 0;
    
    void dfs(int pos) {
        ++_c;
        if (cnt >= ans)
            return;
        if (pos == n + 1)
            ans = min(ans, cnt);
        else
            for (int i = 1; i <= n; i++) {
                if (u[i])
                    continue;
                int fg = 0;
                for (int j = 0; j < g[i].size(); j++)
                    if (u[g[i][j]] == 0) {
                        fg = 1;
                        break;
                    }
                if (fg)
                    continue;
                u[i] = 1;
                s[pos] = i;
                if (c[s[pos]] - c[s[pos - 1]])
                    ++cnt;
                dfs(pos + 1);
                if (c[s[pos]] - c[s[pos - 1]])
                    --cnt;
                u[i] = 0;
                s[pos] = 0;
            }
    }
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> x[0] >> y[0] >> x[1] >> y[1] >> c[i];
            for (int j = x[0] + 1; j <= x[1]; j++)
                for (int k = y[0] + 1; k <= y[1]; k++) a[j][k] = i;
        }
        for (int i = 1; i <= 9; i++)
            for (int j = 1; j <= 9; j++)
                if (a[i][j] && a[i - 1][j] && a[i][j] != a[i - 1][j])
                    g[a[i][j]].push_back(a[i - 1][j]);
        for (int i = 1; i <= n; i++)
            sort(g[i].begin(), g[i].end()), g[i].resize(unique(g[i].begin(), g[i].end()) - g[i].begin());
    
        dfs(1);
    
        cout << ans << endl;
        // cout<<_c<<endl;
    }
    #10019. 「一本通 1.3 例 2」生日蛋糕  
  • 相关阅读:
    UVA11367 Full Tank?
    不均衡样本集问题
    NLP interview
    Linux 指令
    Python 趣题
    Grid Illumination
    动态规划-Minimum Cost to Merge Stones
    Contest 141
    Python join()方法
    Single Number
  • 原文地址:https://www.cnblogs.com/mollnn/p/11602873.html
Copyright © 2011-2022 走看看