跳转至

10 动态规划

10.1 最长上升子序列 LIS

// 返回最长单调上升子序列, O(nlogn)
vector<int> LIS(vector<int>& arr) {
    int len = arr.size() - 1;  // 1-base
    int ans = 0;
    vector<int> low(2);
    low[1] = arr[1], ans = 1;
    for (int i = 2; i <= len; i++) {
        if (arr[i] > low[ans]) {
            low.push_back(arr[i]);
            ans++;
        } else {
            int p = lower_bound(low.begin() + 1, low.end(), arr[i]) - low.begin();
            low[p] = arr[i];
        }
    }
    return move(low);
}

10.2 换根dp

void link(int x, int y) { // 子树y的贡献并入结点x
    dp[x] += dp[y];
}
void cut(int x, int y) { // 结点x删除子树y的贡献
    dp[x] -= dp[y];
}
void dfs1(int x, int fa, vector<vector<int>> &g) { // 统计子树对根的贡献
    dp[x] = 1;
    for (y : g[x]) {
        if (y == fa) continue;
        dfs1(y, x, g);
        link(x, y);
    }
}
void dfs2(int x, int fa, vector<vector<int>> &g) {
    ans[x] = dp[x]; // 进入结点时,统计答案
    for (int y : g[x]) {
        if (y == fa) continue;
        cut(x, y), link(y, x); // 换根
        dfs2(y, x, g);
        cut(y, x), link(x, y); // 回溯
    }
}

10.3 SOSdp

\(O(n\cdot 2^n)\) 处理子集,超集的和。

void sum_over_subsets_dp() {
    // 1. 初始化 DP 数组
    // F[mask] 的初始值就是 A[mask] 本身,因为 mask 是自身的子集
    for (int mask = 0; mask < (1 << N); ++mask) {
        F[mask] = A[mask];
    }

    // 2. 按位进行 DP
    // 外层循环遍历每一位 (维度)
    for (int i = 0; i < N; ++i) {
        // 内层循环遍历所有 mask
        for (int mask = 0; mask < (1 << N); ++mask) {
            // 如果 mask 的第 i 位是 1
            // 那么 F[mask] 的值需要加上 F[mask ^ (1 << i)] 的贡献
            // mask ^ (1 << i) 是 mask 去掉第 i 位的结果,是 mask 的一个子集
            if (mask & (1 << i)) {
                F[mask] += F[mask ^ (1 << i)];
            }
        }
    }
}

void sum_over_supersets_dp() {
    // 1. 初始化
    for (int mask = 0; mask < (1 << N); ++mask) {
        F[mask] = A[mask];
    }

    // 2. 按位进行 DP
    for (int i = 0; i < N; ++i) {
        // 内层循环 mask 需要从大到小
        for (int mask = (1 << N) - 1; mask >= 0; --mask) {
            // 如果 mask 的第 i 位是 0
            // 那么 F[mask] 的值需要加上 F[mask | (1 << i)] 的贡献
            // mask | (1 << i) 是 mask 加上第 i 位的结果,是 mask 的一个超集
            if (!(mask & (1 << i))) {
                F[mask] += F[mask | (1 << i)];
            }
        }
    }
}