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

一.区间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. SSRF(服务端请求伪造)

  2. optparser模块 与 ZIP爆破(Python)

    optparser模块: 为脚本传递命令参数. 初始化: 带 Usage 选项(-h 的显示内容 Usage:): >>> from optparse import OptionPa ...

  3. wamp新建虚拟目录无法运行的解决方法

    操作步骤: 打开 D:\wamp\bin\apache\apache2.4.9\conf\httpd.conf  文件,大概在第242行 把 <Directory /> AllowOver ...

  4. (详细)JAVA使用JDBC连接MySQL数据库(3)-代码部分

    欢迎任何形式的转载,但请务必注明出处. 本节主要内容 项目建立 数据库连接 数据库操作 主函数 点击进入推荐博客(必看) 一.项目建立 如图所示:新建Java Project.Package.Clas ...

  5. QCamera : no such file 问题

    在项目 *.pro中添加即可. QT += core gui QT += multimedia QT += multimediawidgets QT += multimedia ==>对应< ...

  6. 5.创建执行线程的方式之三 :实现Callable 接口

    Callable 接口 一.Java 5.0 在 java.util.concurrent 提供了 一个新的创建执行线程的方式(之前有继承Thread 和 实现Runnable):Callable 接 ...

  7. 4.闭锁 CountDownLatch

    /*CountDownLatch 闭锁*/ CountDownLatch 是一各同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待 闭锁可以延迟线程的进度 直到 其到达终 ...

  8. django启动通过ip或是域名访问

    setting.py里面的ALLOWED_HOSTS = ['localhost','域名','本机ip'] 启动时一般都是命令行 python manage.py runserver [端口号]  ...

  9. Signal Processing and Pattern Recognition in Vision_15_RANSAC:Performance Evaluation of RANSAC Family——2009

    此部分是 计算机视觉中的信号处理与模式识别 与其说是讲述,不如说是一些经典文章的罗列以及自己的简单点评.与前一个版本不同的是,这次把所有的文章按类别归了类,并且增加了很多文献.分类的时候并没有按照传统 ...

  10. 两个linux服务器之间免密登录

    服务器A(假设为10.64.104.11) 免密登录服务器B(10.64.104.22) 1.登录服务器A 2.生成公私钥 ssh-keygen -t rsa 3.将生成的.pub文件发送到服务器B上 ...