动态规划-区间、计数类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等技术内容,立即学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值