zoukankan      html  css  js  c++  java
  • 动态规划_区间DP

    282. 石子合并

    • 状态表示:合并(l)(r)这一区间需要的最小代价
    • 状态计算:
      1. 枚举区间长度
      2. 枚举分割点,将所有状态进行划分
    #include <iostream>
    
    using namespace std;
    const int N = 310;
    int a[N], s[N], dp[N][N];
    
    int main() {
        int n; cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            s[i] = s[i - 1] + a[i];
        }
        // 枚举区间长度
        for(int len = 2; len <= n; len++) {
            // 确定左右端点
            for(int i = 1; i + len - 1 <= n; i++) {
                int l = i, r = i + len - 1;
                dp[l][r] = 1e9;
                // 枚举决策点
                for(int k = l; k < r; k++)
                    dp[l][r] = min (
                        dp[l][r], 
                        dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]
                    );
            }
        }
        cout << dp[1][n];
        return 0;
    }
    

    环形DP

    1068. 环形石子合并

    • 利用倍增,将环转换成线性的
    #include <iostream>
    
    using namespace std;
    const int N = 210, M = N << 1;
    int a[N], s[M], h[M];
    int dp[N][N], f[N][N];
    
    int main() {
        int n; cin >> n;
        for(int i = 0; i < n; i++) cin >> a[i];
        
        for(int i = 0; i < 2 * n; i++) s[i] = a[i % n] + s[i - 1];
        
        
        // for(int i = 0; i < 2 * n; i++) cout << s[i] << " ";
        // cout << endl;
        for(int g = 0; g < n; g++) {
            for(int len = 2; len <= n; len++) {
                for(int i = g; i + len - 1 < 2 * n; i++) {
                    int l = i, r = i + len - 1;
                    dp[l][r] = 1e9, f[l][r] = -2e9;
                    for(int k = l; k < r; k++) {
                        dp[l][r] = min(
                            dp[l][r],
                            dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]
                        );
                        f[l][r] = max(
                            f[l][r], 
                            f[l][k] + f[k + 1][r] + s[r] - s[i - 1]
                        );
                        // cout << l << " " << r << " " << dp[l][r] << " " << endl;;
                    }
                }
            }
        }
        int mi = 1e9, mx = -1;
        for(int i = 0; i < n; i++) {
            mi = min(mi, dp[i][i + n - 1]);
            mx = max(mx, f[i][i + n - 1]);
        }
        cout << mi << endl << mx;
    
        return 0;
    }
    

    1069. 凸多边形的划分

    • 区间DP + 高精度算法
    • 状态表示:从顶点(i)(j)之间划分成(N - 2)个互不相交的三角形的集合
    • 状态计算: (dp[i][j] = min(dp[i][j], dp[i][k] * dp[k][j] + w[i] * w[k] * w[j])
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    typedef long long LL;
    const int N = 55, M = 100, INF = 1e9;
    
    int n;
    LL w[N];
    string dp[M][M];
    
    string add(string a, string b) {
        if(a.size() < b.size()) return add(b, a);
        vector<LL> x, y;
        string ans = "";
        for(LL i = a.length() - 1; i >= 0; i--)  x.push_back(a[i] - '0');
        for(LL i = b.length() - 1; i >= 0; i--) y.push_back(b[i] - '0');
        LL t = 0;
        for(LL i = 0; i < (int)x.size(); i++) {
            t += x[i];
            if(i < (int)y.size()) t += y[i];
            ans += ((t % 10) + '0');
            t /= 10;
        }
        if(t) ans += "1";
        reverse(ans.begin(), ans.end());
        return ans;
    }
    
    string mul(string a, LL b) {
        vector<LL> x;
        string ans = "";
        for(LL i = a.length() - 1; i >= 0; i--) x.push_back(a[i] - '0');
        LL t = 0;
        for(LL i = 0; i < (int)x.size() || t; i++) {
            if(i < (int)x.size()) t += x[i] * b;
            ans += ((t % 10) + '0');
            t /= 10;
        }
        while(ans.back() == '0' && ans.size() > 1) ans.pop_back();
        reverse(ans.begin(), ans.end());
        return ans;
    }
    
    string get_min(string x, string y) {
        if (x.size() == 0) return y;
        if (y.size() == 0) return x;
        if (x.size() > y.size()) return y;
        else if (x.size() < y.size()) return x;
        return x < y ? x : y;
    }
    
    int main() {
        cin >> n;
    
        for(LL i = 1; i <= n; i++) cin >> w[i];
    
        for(LL len = 3; len <= n; len ++) 
            for(LL l = 1; l + len - 1 <= n; l++) {
                LL r = l + len - 1;
                dp[l][r] = "10000000000000000000000000000000";
                for(LL k = l + 1; k < r; k++) {
                    string tmp = "1";
                    tmp = mul(mul(mul(tmp, w[l]), w[k]), w[r]);
                    tmp = add(add(tmp, dp[l][k]), dp[k][r]);
                    dp[l][r] = get_min(dp[l][r], tmp);
                }
            }
        cout << dp[1][n];
        return 0;
    }
    

    区间DP记录方案

    479. 加分二叉树

    • 状态表示:中序遍历为i~j的二叉树的得分的最大值
    • 以根节点的位置作为集合划分的依据
    • 同时记录每颗子树的根
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    const int N = 30;
    int w[N], dp[N][N], g[N][N];
    
    void dfs(int x, int y) {
        if(x > y) return ;
        int k = g[x][y];
        cout << k << " ";
        dfs(x, k - 1);
        dfs(k + 1, y);
    }
    
    int main() {
    
        int n; cin >> n;
    
        for(int i = 1; i <= n; i++) cin >> w[i];
    
        for(int len = 1; len <= n; len ++) {
            for(int l = 1; len + l - 1 <= n; l++) {
                int r = l + len - 1;
                // cout << len << " " << l << " " << r << endl;
                if(len == 1) {
                    dp[l][r] = w[l];
                    g[l][r] = l;
                } else {
                    // 枚举根的位置
                    for(int k = l; k <= r; k++) {
                        // k == l, 说明左子树为空
                        int left = (k == l) ? 1 : dp[l][k - 1];
                        // 同理,k == r, 说明右子树为空
                        int right = (k == r) ? 1 : dp[k + 1][r];
                        if(dp[l][r] < left * right + w[k]) {
                            dp[l][r] = left * right + w[k];
                            // 记录当前子树的根节点
                            g[l][r] = k;
                        }
                    }
                }
            }
        }
        cout << dp[1][n] << endl;
        dfs(1, n);
        return 0;
    }
    
  • 相关阅读:
    log4j.appender.stdout.layout.ConversionPattern
    log4j:WARN No appenders could be found for logger
    Eclipse中TODO的分类,以及自动去除
    Java泛型类型擦除导致的类型转换问题
    Java中泛型数组的使用
    Java泛型中的通配符的使用
    Java泛型类定义,与泛型方法的定义使用
    Java泛型的类型擦除
    jQuery查询性能考虑
    jQuery判断checkbox是否选中
  • 原文地址:https://www.cnblogs.com/Hot-machine/p/13348066.html
Copyright © 2011-2022 走看看