动态规划-区间、计数类DP问题总结

简介: 动态规划-区间、计数类DP问题总结

什么是动态规划

动态规划通过额外的空间将已经搜索过的相似的结果(指某些具有相同性质解的集合)用一个数组存起来,所以DP中的状态转移看上去是某两三个值之间的推导,其实是某两三个集合之间的状态转移!

常见的区间DP问题

石子合并

典型题例:

设有 N 堆石子排成一排,其编号为 1,2,3,…,N。

每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有 4 堆石子分别为 1 3 5 2, 我们可以先合并 1、2 堆,代价为 4,得到 4 5 2, 又合并 1,2 堆,代价为 9,得到 9 2 ,再合并得到 11,总代价为 4+9+11=24;

如果第二步是先合并 2,3 堆,则代价为 7,得到 4 7,最后一次合并代价为 11,总代价为 4+7+11=22。

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

示例 :

4
1 3 5 2
输出:
22

思路

状态表示:集合f[i,j]表示所有将[i,j]合并成一堆的方法集合;属性:min

状态计算:最后一次合并肯定是[i,j]分成两堆,第一堆可以是i, i+1, i+2, ...,j-1;

枚举所有[i,j]中左右两边的堆的方法i~k, k+1~j,最后加上总代价s[j]-s[i -1]

状态转移f[i,j] = min(f[i,j], f[i,k] + f[k+1,j] + s[j] - s[i])

代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 310;
int n, m;
int s[N];  //前缀和
int f[N][N];
int main() {
    cin >> n;
    //处理输入和前缀和
    for (int i = 1; i <=n; i ++) {
        cin >> s[i];
        s[i] += s[i - 1];
    }
    for (int len = 2; len <= n; len ++)  //枚举区间长度,len=1时就是一堆
        for (int i = 1; i + len - 1 <= n; i ++) {
            int j  = i + len - 1;
            f[i][j] = 1e8;  //初始设为无穷大
            for (int k = i; k < j; k ++)
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
        }
    printf("%d\n", f[1][n]);
    return 0;
}

计数类DP问题

整数划分

典型题例:

一个正整数 n 可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中 n1≥n2≥…≥nk,k≥1。

我们将这样的一种表示称为正整数 n 的一种划分。

现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法。

示例 :

输入:
共一行,包含一个整数 n。
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对 109+7 取模。
5
输出:
7

思路

等价于完全背包问题:有一个容量为n的背包,且有n个物品,对应的体积分别为1~n,恰好装满背包的方案数

状态表示:集合f[i,j]表示所有从1~i中选,总体积恰好是j的选法的数量的集合;属性:数量

状态计算:f[i,j] = f[i-1,j] + f[i,j-i] 与完全背包优化方式一样:f[j] = f[j] + f[j-i]

代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N];
int main() {
    cin >> n;
    f[0] = 1;
    for (int i = 1; i <=n; i ++)
        for (int j = i; j <= n; j ++)
            f[j] = (f[j] + f[j - i]) % mod;
    printf("%d\n", f[n]);

充电站

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关文章
|
6月前
|
算法 测试技术 C#
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
【动态规划】【同余前缀和】【多重背包】[推荐]2902. 和带限制的子多重集合的数目
|
6月前
|
人工智能 算法 测试技术
【动态规划】【二分查找】C++算法 466 统计重复个数
【动态规划】【二分查找】C++算法 466 统计重复个数
|
6月前
|
算法 测试技术 C#
【动态规划】LeetCode2111:使数组 K 递增的最少操作次数
【动态规划】LeetCode2111:使数组 K 递增的最少操作次数
|
1月前
动态规划——状态 dp
本文介绍了多个动态规划问题的解法,包括按摩师问题(即打家劫舍),通过 `dp` 数组追踪选与不选的最大收益;打家劫舍 II 将环形问题转化为线性;删除并获得点数问题,将数组处理后按打家劫舍规则求解;粉刷房子问题,使用三维 `dp` 数组追踪每种颜色的最小成本,每个问题都详细说明了状态表示、转移方程及初始化等关键步骤,并附有代码实现。
24 2
|
6月前
|
算法 测试技术 C++
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
【动态规划 区间dp 位运算】3117. 划分数组得到最小的值之和
|
6月前
|
算法 测试技术 C#
【状态机dp 动态规划】100290. 使矩阵满足条件的最少操作次数
【状态机dp 动态规划】100290. 使矩阵满足条件的最少操作次数
|
6月前
|
算法 测试技术 C#
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目
|
6月前
|
算法 测试技术 C#
【map】【动态规划】LeetCode2713:矩阵中严格递增的单元格数
【map】【动态规划】LeetCode2713:矩阵中严格递增的单元格数
|
6月前
|
算法 测试技术 C#
二分查找|前缀和|滑动窗口|2302:统计得分小于 K 的子数组数目
二分查找|前缀和|滑动窗口|2302:统计得分小于 K 的子数组数目
|
Cloud Native Go
801. 使序列递增的最小交换次数:动态规划
这是 力扣上的 801. 使序列递增的最小交换次数,难度为 困难。