动态规划?

模型:

最长上升子序列

朴素做法:

for (int i = 1; i <= n; i++) {
    dp[i] = 1;
    for (int j = 1; j < i; j++) {
        if (a[j] < a[i]) {
            dp[i] = max(dp[i], dp[j] + 1);
        }
    }
}
for (int i = 1; i <= n; i++) {
    ans = max(ans, dp[i]);
}

dp优化

int dp[NMAX], len;
dp[len = 1] = a[1];
for(int i = 2;i <= n;i += 1) {
    if(a[i] > dp[len]) {
        dp[++len] = a[i];
    } else {
        *lower_bound(dp + 1, dp + len + 1, a[i]) = a[i];
        //解引用这个地址
    }
}

当然了,你也可以选择树状数组优化

最长公共子序列

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        if (a[i] == b[j]) {
            dp[i][j] = dp[i - 1][j - 1] + 1;
        } else {
            dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
}

你要怎么求出转移方程

for (int i = 1; i <= a.size(); i++) {
    dp[i][0] = i;
}
for (int i = 1; i <= b.size(); i++) {
    dp[0][i] = i;
}
for (int i = 1; i <= a.size(); i++) {
    for (int j = 1; j <= b.size(); j++) {
        if (a[i - 1] == b[j - 1]) {
            dp[i][j] = dp[i - 1][j - 1];
        } else {
            dp[i][j] = min(min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i][j - 1]) + 1;
        }
    }
}

背包问题

可拆分

struct Item {
    int c, w;
};
Item item[1010];
bool cmp(Item a, Item b) {
    return a.w * b.c > b.w * a.c; // 比较单位体积物品价值
}
int main() {
    //省略部分输入代码
    sort(item + 1, item + 1 + N, cmp); // 先进行排序
    double ans = 0;
    for(int i = 1; i <= N; i++) { // 单位体积价值越高越优先放入背包
        if(V <= item[i].c) { // 背包将被当前物品装满
            ans += (double)item[i].w / item[i].c * V;
            V = 0; // 剩余容量变为 0
            break; // 循环结束
        } else { // 直接将物品放入背包
            ans += item[i].w; // 总价值增加
            V -= item[i].c; // 背包剩余容量减少
        }
    }
    //省略部分输出代码
    return 0;
}

01背包

for (int i = 1; i <= N; i++) {
    for (int j = V; j >= c[i]; j--) {  // 循环从背包容积 V 开始,到 c[i] 结束,从大到小枚举
        dp[j] = max(dp[j], dp[j - c[i]] + w[i]); 
        // 对于 j 来说,更小的位置 j - c[i]
        // 还未更新,里面存储的是上一层的值,因此可以进行计算
    }
}

多重背包问题

状态压缩动态规划

int ncnt = 0;
// 二进制拆分
for (int i = 1; i <= N; ++i) {
    int k;
    // 找到最大的 k
    for (k = 1; n[i] - (1 << k) + 1 > 0; ++k) {
        nc[ncnt] = (1 << (k - 1)) * c[i];
        nw[ncnt] = (1 << (k - 1)) * w[i];
        ++ncnt;
    }
    --k;
    // 最后一组
    nc[ncnt] = (n[i] - (1 << k) + 1) * c[i];
    nw[ncnt] = (n[i] - (1 << k) + 1) * w[i];
    ++ncnt;
}
// 01 背包
for (int i = 0; i < ncnt; ++i) {
    for (int j = V; j >= nc[i]; --j) {
        dp[j] = max(dp[j], dp[j - nc[i]] + nw[i]);
    }
}

完全背包问题

for (int i = 1; i <= N; i++) {
    for (int j = c[i]; j <= V; j++) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }
}

多维背包问题

for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k <= W; k++) {
            dp[i][j][k] = dp[i - 1][j][k];
            if (j >= v[i] && k >= w[i]) {
                dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - v[i]][k - w[i]] + c[i]);
            }
        }
    }
}

分组背包

for(int i = 1; i <= d; ++i){ // 枚举组
    for(int k = target; k >= 0; --k){ // 枚举背包体积进行转移
        for(int j = 1; j <= f; ++j){ // 枚举组内的所有物品
            if(k >= c[i][j])
                dp[k] = max(dp[k], dp[k - c[i][j]] + v[i][j]); // 此时 dp[k-j] 一定表示上一组的状态
        }
    }
}

树形背包

本质上是一个树上的dfs
转移方程如下
f[i][j]=max{f[i+siz[i]][j],f[i+1][j−w[i]]+v[i]}.
也可以使用dfs序做
把从根开始搜索,将所有的点按照其搜索到的顺序排列到序列中,如果点i的DFS序为x,就表示是第x个被搜索到的
记录dfs序以及子树大小

DAG上的dp

DAG:有向无环图
需要搭配拓扑排序

int toposort() {
    queue<int> q;
    for (int i = 1; i <= n; ++i) {
        if (deg[i] == 0) {
            // 初始化 f[i]
            q.push(i);
        }
    }
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i; i = G[i].next) {
            int v = G[i].to;
            // 用 f[u] 更新 f[v]
            if (--deg[v] == 0) q.push(v); // v 的 DP 值已经计算完成,可以用于更新其他点
        }
    }
    // 用每个点的 DP 值计算最终的答案
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值