本博客部分内容参考:《算法竞赛进阶指南》

一.区间DP

  划重点:

  以前所学过的线性DP一般从初始状态开始,沿着阶段的扩张向某个方向递推,直至计算出目标状态。

  区间DP也属于线性DP的一种,它以“区间长度”作为DP的“阶段”,使用两个坐标(区间的左、右端点)描述每个维度。在区间DP中,一个状态由若干个比它更小且包含于它的区间所代表的状态转移而来,因此区间DP的决策往往就是划分区间的方法。区间DP的初态一般就由长度为1的“元区间”构成。

  下面介绍一道经典题:石子合并

题目描述:

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

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

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

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

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

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

输入格式

第一行一个数N表示石子的堆数N。

第二行N个数,表示每堆石子的质量(均不超过1000)。

输出格式

输出一个整数,表示最小代价。

数据范围

1≤N≤300,1≤N≤300

输入样例:

   

输出样例:


DP思路:

  状态表示:我们用数组F[i,j]来描述DP状态,表示闭区间[i,j]的石子合并所需的最小代价。

  对于任意时刻,任意区间[i,j]的石子,若第l堆与第r堆石子已经被合并,那么说明l~r之间任意一堆石子都已经被合并,这样才能保证l与r相邻。而要使第l堆与第r堆合并,必然存在一个整数k,使得第l~k堆石子先合并成一堆,第k+1~r堆石子合并成一堆,然后这两堆石子才能合并。于是,划分点k便是转移的决策,区间长度当然要作为DP的阶段,那么便可以得到状态转移方程。

  状态计算:状态转移方程:F[l,r]=min{F[l,k]+F[k+1,r]}+ΣAi。其中k∈[l,r),i∈[l,r]。ΣAi表示区间[l,r]内每个数的和。对于求这个和,我们可以用一维的前缀和来实现:用数组sum[i]表示到以第i个数字结尾的前缀和,便可以得到ΣAi=sum[r]-sum[l-1]。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = +;
int f[N][N],sum[N]; inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch<=''&&ch>='')x=(x<<)+(x<<)+(ch^),ch=getchar();
return x*f;
} int main()
{
int n=read();
for(int i=;i<=n;i++)
{
int x=read();
sum[i]=sum[i-]+x;
} for(int m=;m<=n-;m++) //注意DP循环中的各个边界
for(int i=,j=i+m;j<=n;i++,j++)
{
int minn=2e+;
for(int k=i;k<=j-;k++)
minn=min(minn,f[i][k]+f[k+][j]);
f[i][j]=minn+sum[j]-sum[i-];
} printf("%d\n",f[][n]);
return ;
}

二.计数类DP

  计数类DP和数位统计DP两类问题都特别强调“不重不漏”,统计对象的可能情况一般比较多,通常需要精确的划分和整体性的计算。因此,使用动态规划抽象出问题中的“子结构”和推导的“阶段”,将有助于我们准确而高效地进行求解。

  下面介绍一道经典题:整数划分

  这里介绍这道题的两种DP解法:

  注意:类似于整数划分这种计数类DP考虑顺序(排列)与不考虑顺序(组合)是不同的算法

1.完全背包解法

  状态表示:
  F[i,j]表示只从1~i中选,且总和等于j的方案数。

  状态转移方程:
  F[i,j] = F[i - 1,j] + F[i - 1,j - i];

  对于任意一种状态F[i,j]是由什么状态转移而来的问题,我们可以类似于背包问题地考虑Ai这个数的选与不选两种情况。而F[i,j]就是这两种情况的数量之和。

  如果不选,那么就是从前i-1个数中选出若干个数使和为j的方案数;如果选,那么就是从前i+1个数中选出若干个数使和为j-i的方案数。

  那么再类比背包问题中将二维数组优化成一维的方式,我们就可以得到一维数组的状态转移方程:F[j]=F[j]+F[j - i];

  最后再注意一下边界:F[0] = 1。

  代码如下:

#include <iostream>
#include <algorithm> using namespace std; const int N = , mod = 1e9 + ; int n;
int f[N]; int main()
{
cin >> n; f[] = ;
for (int i = ; i <= n; i ++ )
for (int j = i; j <= n; j ++ )
f[j] = (f[j] + f[j - i]) % mod; cout << f[n] << endl; return ;
}

2.
  状态表示:
  F[i,j]表示总和为i,总个数为j的方案数

  状态转移方程:
  F[i,j] = F[i - 1,j - 1] + F[i - j,j];

  参考上面一种方法的DP思路,我们可以把所有状态的集合划分成两类,一类是选择了数字“1”的,另一类是不选择数字“1”的。那么F[i,j]仍然是这两类情况之和。

  如果不选“1”,那么就是从j-1个数中选出总和为i-1的方案数;如果选“1”,那么就是从j个数中选出总和为i-j的方案数。

  最后再将总和为n的所有情况求和即可。

  代码如下:

#include <iostream>
#include <algorithm> using namespace std; const int N = , mod = 1e9 + ; int n;
int f[N][N]; int main()
{
cin >> n; f[][] = ;
for (int i = ; i <= n; i ++ )
for (int j = ; j <= i; j ++ )
f[i][j] = (f[i - ][j - ] + f[i - j][j]) % mod; int res = ;
for (int i = ; i <= n; i ++ ) res = (res + f[n][i]) % mod; cout << res << endl; return ;
}

三.数位统计DP

  这类问题其实与DP关系不大,大多都是小学奥数的问题,这里也不多讲,直接放经典题:计数问题

  这道题由于数据范围的限制,直接枚举肯定是过不去的(否则那就是入门题了)。对于这个问题的解法,为了减少上下界的限制,我们不妨计算1~a-1和1~b中各个数字出现的次数,再作一个差,就可以得到答案了。对于统计数字的方法,我们先举一个的例子:

  统计数字“1”在五位数abcde中出现的次数:

  首先看“1”在第一位出现了几次,比较a与1的大小,若a=1,显然是abcde-9999;若a>1,则是10000;

  再看第二位,先加上二位数ab-1乘上1000,再比较b与“1”的大小,若b=1,则再加上三位数cde,若b>1,则加上1000;

  ……

  以此类推,便可以求出第三位、第四位与第五位的次数,最后求一个和即可。

  代码实现如下:

  

动态规划——区间DP,计数类DP,数位统计DP的更多相关文章

  1. $CH5302$ 金字塔 区间$DP$/计数类$DP$

    CH Sol f[l][r]表示l到r这段区间对应的金字塔结构种数 发现是f[l][r]是可以由比它小的区间推出来的 比如已知f[l+1][k],f[k+1][r],不难想到f[l][r]+=f[l+ ...

  2. 0x5C 数位统计DP

    怎么说,数位DP还是我的噩梦啊,细节太恐怖了. 但是这章感觉又和之前的学的数位DP有差异?(应该是用DP预处理降低时间复杂度,好劲啊,不过以前都是记忆化搜索的应该不会差多少) poj3208 f[i] ...

  3. $BZOJ1799\ Luogu4127$ 月之谜 数位统计$DP$

    AcWing Description Sol 看了很久也没有完全理解直接$DP$的做法,然后发现了记搜的做法,觉得好棒! 这里是超棒的数位$DP$的记搜做法总结   看完仿佛就觉得自己入门了,但是就像 ...

  4. $Poj3208$ 启示录 数位统计$DP$

    Poj  AcWing Description Sol  这题长得就比较像数位$DP$叭. 所以先用$DP$进行预处理,再基于拼凑思想,通过"试填法"求出最终的答案. 设$F[i] ...

  5. Codeforces Round #277 (Div. 2)D(树形DP计数类)

    D. Valid Sets time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  6. SDOI2010代码拍卖会 (计数类DP)

    P2481 SDOI2010代码拍卖会 $ solution: $ 这道题调了好久好久,久到都要放弃了.洛谷的第五个点是真的强,简简单单一个1,调了快4个小时! 这道题第一眼怎么都是数位DP,奈何数据 ...

  7. $CF559C\ Gerald\ and\ Fiant\ Chess$ 计数类$DP$

    AcWing Description 有个$H$行$W$列的棋盘,里面有$N$个黑色格子,求一个棋子由左上方格子走到右下方格子且不经过黑色格子的方案数. $1<=H,M<=1e5,1< ...

  8. CH5E26 扑克牌 (计数类DP)

    $ CH~5E26~\times ~ $ 扑克牌: (计数类DP) $ solution: $ 唉,计数类DP总是这么有套路,就是想不到. 这道题我们首先可以发现牌的花色没有价值,只需要知道每种牌有 ...

  9. $Poj1737\ Connected\ Graph$ 计数类$DP$

    AcWing Description 求$N$个节点的无向连通图有多少个,节点有标号,编号为$1~N$. $1<=N<=50$ Sol 在计数类$DP$中,通常要把一个问题划分成若干个子问 ...

随机推荐

  1. CentOS下 .Net Core 1.0 升级到 3.0 遇到的一个小问题

    之前.net core 1.0的安装方式,不是用yum方式安装的,所以,在用yum安装3.0之后,用dotnet --version还是1.0的版本,想起了之前 做过链接映射dotnet目录,删除之前 ...

  2. Genymotion上运行ARM架构Android项目

    问题 Genymotion是x86的模拟器,在集成一些第三方的SDK时需要添加一些放在armeabi.armeabi-64文件夹下面的.so文件,这些文件在arm架构下才能运行.这时模拟器就不能用了. ...

  3. sql 给相同属性的数据排序

    UPDATE b SET OrderIndex = a.OrderIndex FROM ( SELECT RTRIM(ROW_NUMBER() OVER ( PARTITION BY [ItemID] ...

  4. vue——store全局存储

    业务场景:刷新页面时,首次拉取所有配置,存储到store状态管理用于全局调用: import Vue from 'vue' import Vuex from 'vuex' import userInf ...

  5. 【SpringMVC】入门

    一.概述 1.1 SpringMVC是什么 1.2 MVC在b/s系统的应用 1.3 SpringMVC 原理 二.入门程序 2.1 需求 2.2 引入依赖 2.3 前端控制器 2.4 springm ...

  6. 查看mysql的bin-log日志

    1.查看有哪些binlog show binary logs; show master logs; 2.如何查看log_bin中的内容 show binlog events; 查看第一个binlog的 ...

  7. Java学习第二天之Java程序的基本规则

    一.Java程序的组织形式 Java程序是一种纯粹的面向对象的程序设计语言,因此Java程序必须以类(即class)的形式存在,类(class)是Java程序的最小程序单位.Java程序不允许可执行性 ...

  8. 基于OPENldap搭建postfix 虚拟用户

    本文首发: https://www.somata.work/2019/DependOPENldapBuildPostfixVirtualMailUser.html postfix + dovecot ...

  9. CSS复合选择器和div盒子模型

    一.复合选择器(3种) 1.交集复合选择器 特点:由2个选择器组成,其中第一个必须是标签选择器,第二个是类或id选择器.两个选择器之间没有空格(有空格属于层级选择器) <h3 class=&qu ...

  10. 大海航行靠舵手 华为云靠什么征服K8S?

    Kubernetes 是Google开源的容器集群管理系统或者称为分布式操作系统.它构建在Docker技术之上,为容器化的应用提供资源调度.部署运行.服务发现.扩容缩容等整一套功能,本质上可看作是基于 ...