加载中...

acwing算法基础课V

第五章 动态规划(一)

  1. 非常常见的dp的模型, 背包模型.
  2. 不同类型的dp 线性dp 计数dp 等....

一个物体 有 体积 \(v_i\) 和价值 \(w_i\) 用w表示权重的意思.

每件物品仅用一次.

总体积小于等于 \(V\) 目标是让总价值 \(W\) 最大, 最大是多少.

  1. 01背包. 每个物品最多只用一次.
  2. 完全背包 每件物品无限个
  3. 多重背包 n个. 优化问题.
  4. 分组背包. 每一组最多一个物品.

问背包可以装的下的情况下, 最多可以装多少物品.

闫式DP

  1. 状态表示 f(i,j)

    1. 集合 所有选法: 前i个物品中, 总体积 \(\le\)j

    2. 属性 max / min / 数量.

      存储的是所有选法的最大值.

  2. 状态计算

    1. 状态转移怎么来的?
    2. 集合划分. 不重 不漏.
    3. f(i,j) 不含i 和 含 i

DP优化.

背包问题

01背包问题

f(i,j) = max(f(i-1,j), f(i-1,j-v[i])+w[i])

int v[N3], w[N3];
int f[N3][N3];
int main() {
    int n = read(), m = read();
    rep(i, 1, n + 1) v[i] = read(), w[i] = read();
    rep(i, 1, n + 1) {
        rep(j, 1, m + 1) {
            f[i][j] = f[i - 1][j]; //第一个集合;
            if (v[i] <= j)
                f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

优化成一维 看变量什么时候发生的改变.

int v[N3], w[N3];
int f[N3]; // 可以优化掉一维的数量, 因为这里存储的还是不变量.
int main() {
    int n = read(), m = read();
    rep(i, 1, n + 1) v[i] = read(), w[i] = read();
    rep(i, 1, n + 1) {
        per(j, m + 1, v[i]) { // 注意 从后到前循环, 同时 最多循环到 v[i]
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[m] << endl;
    return 0;
}

用 rep 和 per的时候也挺开心的.

完全背包问题

img

int v[N3], w[N3], f[N3];
int main() {
    int n = read(), m = read();
    rep(i, 0, n)v[i] = read(), w[i] = read();
    rep(i, 0, n) {
        rep(j, v[i], m + 1) { //只要变化一下, 就能选择到很多了.
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    O(f[m]);
}
多重背包问题

具体的个数.

滑动窗口最大值, 单调队列优化? ???

int f[N3][N3], w[N3], v[N3], s[N3];
int main() {
    int n = read(), m = read();
    rep(i, 1, n + 1) v[i] = read(), w[i] = read(), s[i] = read();
    rep(i, 1, n + 1)  // O(n)
        rep(j, 0, m + 1)  // O(m)
        for (int k = 0; k <= s[i] && k * v[i] <= j; k++)
            f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); //O(k)
            // 第三重循环直接暴力检验一下. 
    O(f[n][m]);
}
多重背包问题II

二进制优化方式. 拆分.成01背包问题.

\(s->log(s)\) 的复杂度

const int N = 25000, M = 2010;
int n, m, cnt;
int v[N], w[N], f[M];
int main() {
    n = read(), m = read();
    rep(i, 0, n) {
        int a = read(), b = read(), s = read();
        int k = 1;
        while (k <= s) {
            s -= k, cnt++; // 每个物品进行拆分.
            v[cnt] = a * k, w[cnt] = b * k; // 按照 cnt 进行放置.
            k *= 2;
        }
        if (s) ++cnt, v[cnt] = a * s, w[cnt] = b * s; 
    }
    n = cnt;
    rep(i, 1, n + 1) {
        per(j, m + 1, v[i])
            f[j] = max(f[j], f[j - v[i]] + w[i]); // 简单的01背包问题.
    }
    O(f[m]);
}
分组背包问题

Q: N 和 V . 多组物品, 每组只能选一个. 体积 \(v_{ij}\) 价值 \(w_{ij}\) , 问背包最大价值?

分组直接 多次循环就好.

每组每个都取一遍 for(每个组) for(每个体积) for(每个物品) 所以是 \(O(n^3)\)的复杂度.

// 然后 组不同了再换是吗? 怎么优化到一维的呢?

const int N = N2;
int f[N], v[N][N], w[N][N], s[N];
int n, m;
int main() {
    n = read(), m = read();
    rep(i, 0, n) {
        s[i] = read();
        rep(j, 0, s[i]) v[i][j] = read(), w[i][j] = read();
    }
    rep(i, 0, n) {
        per(j, m + 1, 0) {
            rep(k, 0, s[i]) {
                if (v[i][k] <= j)
                    f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
                // 这个可以保证分组, 因为可以保证前面的不被覆盖..
            }
        }
    }
    O(f[m]);
    return 0;
}

线性DP

数字三角形
最长上升子序列
最长上升子序列II
最短编辑距离
编辑距离

区间DP

石子合并

计数类DP

整数划分

数位统计DP

计数问题

状态压缩DP

蒙德里安的梦想
最短Hamilton路径

树形DP

没有上司的舞会

记忆化搜索

滑雪.
posted @ 2022-02-11 12:51  benenzhu  阅读(189)  评论(0)    收藏  举报