[转]DP问题各种模型的状态转移方程

1(最长公共子串(注意和最长公共子序列区别))

两个字符串str1和str2,长度分别为(l1,l2)

dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度,由于下面涉及到i-1和j-1,那么这个时候我们一般从i=1和j=1开始到i<=len1, j<=len2。

if(str[i-1]=str[j-1])

dp[i][j]=dp[i-1][j-1]+1;

if(str[i-1]!=str[j-1])

dp[i][j]=0;

0 ;                                       i = 0或j= 0;

就有          dp  =        dp[i][j] = dp[i-1][j-1] + 1; i > 0且j> 0 且ch1[i-1]= ch2[j-1];

dp[i][j]=  0;                        i > 0且j> 0 且ch1[i-1]!= ch2[j-1];

注意dp[i][0]=0(0<=i<=max(l1,l2);dp[0][i]=0(0<=i<=max(l1,l2);

最长公共字串要求在原来字符中满足是连续的,最长公共子序列则不要求

最长公共子序列:

根据最长公共子序列问题的性质,我们可以规定dp[i][j]为字符串1的前i个字符和字符串2的前j个字符的最长公共子序列的长度,  由于下面涉及到i-1和j-1,那么这个时候我们一般从i=1和j=1开始到i<=len1, j<=len2。

1ch1[i-1] = ch2[j-1] ,那么dp[i][j]= dp[i-1][j-1] + 1;

2 ch1[i-1] != ch2[j-1] ,那么我们知道ch1[i]和ch2[j]不可能在同一个公共子序列里出现,那么这个时候的最长的子序列可能以ch1[i]或ch2[j]结尾,那么由于dp[i][j]= max {dp[i-1][j] , dp[i][j-1]};

这个时候所有i=0或j=0的dp[i][j]= 0;

0 ; i = 0或j= 0;

就有          dp  =       dp[i][j] = dp[i-1][j-1] + 1; i > 0且j> 0 且ch1[i-1]= ch2[j-1];

dp[i][j]= max {dp[i-1][j] , dp[i][j-1]};i > 0且j> 0且ch1[i-1]!= ch2[j-1];

2(最长上升或下降子序列)

给定一个序列a1,a2..........an;

dp[i]表示以ai结尾的最长上升子序列长度(下降相反)

核心代码:

for(i=1;i<=n;i++){

dp[i]=1;

for(k=1;k<i;k++){

if(ak<ai&&dp[i]<dp[k]+1)

dp[i]=dp[k]+1;

}

}

注意最长不上升或不下降子序列问题

3(最大子序列的和问题)

给定一个序列a1,a2..........an;

求子序列的和最大问题dp[i]表示以ai结尾的子序列和,max为最大子序列和

核心:

1如果输入的数据全部为负数则最大值就是序列中的一个最大值

2如果有正数

for(i=1;i<=n;i++){

dp[i]=dp[i-1]+ai;

if(dp[i]<0)

dp[i]=0;

if(max<dp[i])

max=dp[i];
}

4(数塔问题)

给定一个数组s[n][m]构成一个数塔求从最上面走到最低端经过的路径和最大

我么采用至底向上的思路求解问题(注意从倒数第二行开始)

dp[i][j]表示走到第i行第j个的最大值

那么就有dp[i][j]=max{dp[i-1][j-1],dp[i-1][j]}+s[i][j];

for(i=n-1;i>=1;i--){

for(j=1;j<=i;j++){

dp[i][j]=max{dp[i-1][j-1],dp[i-1][j]}+s[i][j]

}

}

最后dp[1][1]即为最大值

5(01背包问题)

有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是c[i]。求解将哪些物品装入背包可使价值总和最大。

我们知道对于没一件物品我们有两种可能就是放与不放

dp[i][j]表示第i件物品放入容量为j的背包所得的最大价值

dp[i][j]=max{dp[i-1][j-v[i]]+c[i],dp[i-1][j]};

这里我们从j=V倒推回来的话可以优化成

dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

核心代码:

for(i=1;i<=n;i++){

for(j=V;j>=0;j--){

if(j>=v[i])

dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

}

}

dp[v]即为最大的价值

6(完全背包问题)

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这时候对于没见物品就不是放与不放的问题了,而是放0件1件.......

这时候我们可以像01背包一样

dp[i][j]表示容量为j的背包第i件物品是否要再一次放入所以我们要从0-V顺序循环

dp[i][j]=max{dp[i-1][j-v[i]]+c[i],dp[i-1][j]}(注意这里和01背包一样但是求解的过程不同)

优化后:dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

核心代码:

for(i=1;i<=n;i++){

for(j=v[i];j<=v;j++){//注意这里是从v[i]开始到V

if(j>=v[i])

dp[j]=max{dp[j],dp[j-v[i]]+c[i]};

}

}

注意这列求出的dp[v]是最大的因为一直叠加

7(多重背包问题)

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。

重点:令dp[i][j]表示前i种物品恰放入一个容量为j的背包的最大价值

状态转移方程:dp[i][j]=max{dp[i-1][v-k*v[i]]+k*c[i]|0<=k<=n[i]};(k表示第i种物品放入k件);

核心代码:

for(i=1;i<=n;i++){

for(j=v;j>=0;j--){

for(k=1;k<=n[i];k++){

if(j>=k*v[i])

dp[i][j]=max(dp[i-1][v-k*v[i]]+k*c[i])
                         }

}

8: (二维费用的背包问题)

二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i];

费用加了一维,只需状态也加一维即可。设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。

状态转移方程就是:

f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

9(最大子段和问题(最大子序列的和不同))

给定一个序列为a1,a2,a3......an;

要求:求出这个序列里面找到一个子段和最大

dp[i]表示以第i个元素结束,求出所有的“以第i个元素结束的连续数组最大和dp[i]

就有:

1如果dp[i-1]>0,无论ai为何值,有dp[i]=dp[i-1]+ai;

2如果dp[i-1]<=0;舍弃,重新令dp[i]=ai;(因为dp[i-1]为负数无论ai为什么值加上去都会减少)

状态转移方程:dp[i]=dp[i-1]+ai (dp[i-1]>0)

dp[i]=ai(dp[i-1]<=0)

12(最大m子段和)

在限制条件增加一维时,可以将状态也相应的增加一维,来进行状态转移

以dp[i][j]表示以第i个元素为结尾,使用j个子段所能达到的最大值(这一维的状态,正是对应了新的限制条件!)这样就很容易写出

状态转移方程:

dp[i][j]= max{ dp[i - 1][j] + a[i], dp[i - k][j - 1] + a[i]}( j - 1 <= k <n - m + j).

1  dp[i - 1][j] + a[i] (把第i个元素包含在最后一个子段内)

2  dp[i - k][j - 1] + a[i], j - 1 <= k < n - m + j(第i个元素单独为一子串)

13矩阵连乘

问题描述:                 给定一序列的矩阵要求找到一种矩阵连乘的顺序,使得连乘的次数最少

思路:                      建立递推表达式,利用动态规划的方式(m[i][j]表示第i个矩阵至第j个矩阵这段的最优解,还有对于两个矩阵M(i,j)*S(j,k)则需要i*j*k次乘法)

1显然如果i=j,则m[i][j]这段中就一个矩阵,需要计算的次数为0;

2如果i < j,则m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]},其中k,在i与j之间游荡,所以i<=k<j;

3因为你要保证在计算m[i][j]查找m[i][k]和m[k+1][j]的时候,m[i][k]和m[k+1][j]已经计算出来了

所以有动态转移方程:

m[i][j]={ 0 , i=j};

m[i][j]={min{m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]} , i!=j};

m[1][n]即为最终求解

白书上面写道:记忆华搜索固然没有问题,但如果要写成递推,无论按照i还是j的递增或递减均不争确。正确的方法是按照j-i递增的顺序递推,因为长区间的值依赖于短区间的值

模板:

int dp[MAXN][MAXN];//存储最小的就算次数
int s[MAXN][MAXN];//存储断点,用在输出上面

核心代码:

int i , j ,tmp;

for(l= 2 ; l <= n ; l++){//j-i的长度,由于长度为1是相同的矩阵那么为0不用计算
                         for(i = 1 ; i <= n-l+1 ; i++){//由于j-i =l - 1 , 那么j的最大值为n,所以i上限为 n - l+1;
                              j = i+l-1;//由于j-i = l - 1 , 那么j = l+i-1
                             dp[i][j] = dp[i+1][j] + r[i]*c[i]*c[j];//初始化,就是k = i;
                             s[i][j] = i;
                             for(k = i+1 ; k < j ; k++){//循环枚举k i < k < j
                                 tmp = dp[i][k] + dp[k+1][j] + r[i]*c[k]*c[j];
                                 if(dp[i][j] > tmp){
                                    dp[i][j] = tmp;//更新为最小值
                                    s[i][j] = k;
                            }
                        }
                    }
               }

输出:

//递归调用输出
       void output(int i , int j){
              if(i == j){
                  printf("A%d" , i);//当两个相等的时候就不用继续递归就输出A
                  return;//返回上一层
              }

else{
                  printf("(");
                  output(i , s[i][j]);
                  printf(" x ");
                  output(s[i][j]+1 , j);
                  printf(")");
             }     
      }

14区间DP

区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合,求合并后的最优值。
         1  设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
         2  最小区间F[i,i]=0(一个数字无法合并,∴代价为0)

3  每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段

4《区间DP模板,代码》
              for(int p = 1 ; p <= n ; p++){//p是区间的长度,作为阶段
           for(int i = 1 ; i <= n-p ; i++){//i是穷举区间的起点
               int j = i+p;//j为区间的终点
               for(int k = i+1 ; k < j ; k++)//状态转移
                   dp[i][j] = min{dp[i][k]+dp[k+1][j]+w[i][j]};//这个是看具体的状态转移方程

或 dp[i][j] = max{dp[i][k]+dp[k+1][j]+w[i][j]};//求最大

或  dp[i][j] = min{dp[i][k]+dp[k][j]+w[i][j]}//有的是要从k开始不是k+1

}
       }

时间: 2024-11-10 15:34:40

[转]DP问题各种模型的状态转移方程的相关文章

DP问题各种模型的状态转移方程 (转)

1(最长公共子串(注意和最长公共子序列区别)) 两个字符串str1和str2,长度分别为(l1,l2) dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度,由于下面涉及到i-1和j-1,那么这个时候我们一般从i=1和j=1开始到i<=len1, j<=len2. if(str[i-1]=str[j-1]) dp[i][j]=dp[i-1][j-1]+1; if(str[i-1]!=str[j-1]) dp[i][j]=0; 0 ;              

Mark一下, dp状态转移方程写对,但是写代码都错,poj 1651 poj 1179

dp题: 1.写状态转移方程; 2.考虑初始化边界,有意义的赋定值,还没计算的赋边界值: 3.怎么写代码自底向上计算最优值 今天做了几个基础dp,全部是dp方程写对但是初始化以及计算写错 先是poj 1651 其实就是个赤裸裸的矩阵连乘,dp方程很容易写出 dp[i][j]=min(dp[i][k]+dp[k+1][j]+r[i]*c[k]*c[j],dp[i][j]); 先贴两个个二逼的代码,mark下自己多么的二逼: 二逼一:在计算的时候使用了还没有算出来的值,模拟下就知道第一重循环里算dp

hdu 1185 状压dp 好题 (当前状态与上两行有关系)

/* 状压dp 刚开始&写成&&看了好长时间T0T. 状态转移方程 dp[i][k][j]=Max(dp[i][k][j],dp[i-1][l][k]+num[i][j]);(第i行的第j个状态有上一行的第k个状态得到) num[i][j]有两个功能,第一:判断第i行第j个状态是否合法 第二:判断第i行第j个状态的数目 */ #include<stdio.h> #include<string.h> #define N 110 int dp[N][N][N];

[dp专题] AC自动机与状态压缩dp的结合

最近做到好几道关于AC自动机与状态压缩dp的结合的题,这里总结一下. 题目一般会给出m个字符串,m不超过10,然后求长度为len并且包含特定给出的字符串集合的字符串个数. 以HDU 4758为例: 把题意抽象为:给出两个字符串,且只包含两种字符 'R'.'D',现在求满足下列条件的字符串个数:字符串长度为(m+n),其中包含n个'D',m个'R'. 如果不用AC自动机来做,这道题还真没法做了,因为不管怎样都找不到正确的dp状态转移方程. 而如果引入AC自动机,把在AC自动机上的结点当做dp的一个

【转载】 HDU 动态规划46题【只提供思路与状态转移方程】

1.Robberies 连接 :http://acm.hdu.edu.cn/showproblem.php?pid=2955 背包;第一次做的时候把概率当做背包(放大100000倍化为整数):在此范围内最多能抢多少钱  最脑残的是把总的概率以为是抢N家银行的概率之和- 把状态转移方程写成了f[j]=max{f[j],f[j-q[i].v]+q[i].money}(f[j]表示在概率j之下能抢的大洋); 正确的方程是:f[j]=max(f[j],f[j-q[i].money]*q[i].v)  其

《概率统计》14.状态解码:隐马尔科夫模型隐含状态揭秘

隐含状态解码问题的描述 上一篇我们讲完了概率估计问题,这里我们再来讲一下隐马尔科夫模型的状态解码问题. 解码 Decoding,就是给定一个已知的观测序列,求它最有可能对应的状态序列.那么用形式化的语言来说,就是已知模型λ = (A, B, π)和观测序列\(O = (o_{1},o_{2},...,o_{T})\),求使得条件概率P(I|O)最大的隐状态序列I = (\(i_{1},i_{2},...,i_{T}\)) 我们一步一步地来,先不考虑输出观测,仅仅只考虑隐状态的转移,来体会一下思路

动态规划(下):如何求得状态转移方程并进行编程实现?

动态规划(下):如何求得状态转移方程并进行编程实现? 状态转移方程和编程实现 这里面求最小值的 min 函数里有三个参数,分别对应我们上节讲的三种情况的编辑距离,分别是:替换.插入和删除字符.在表格的右下角标出了两个字符串的编辑距离 1. 我们假设字符数组 A[]和 B[]分别表示字符串 A 和 B,A[i]表示字符串 A 中第 i 个位置的字符,B[i]表示字符串 B 中第 i 个位置的字符.二维数组 d[,]表示刚刚用于推导的二维表格,而 d[i,j]表示这张表格中第 i 行.第 j 列求得

简单dp的状态转移方程集合

1.对于任一种N的排列A,定义它的E值为序列中满足A[i]>i的数的个数.给定N和K(K<=N<=1000),问N的排列中E值为K的个数. dp[i][j]表示i个数的排列中E值为j的个数.假设现在已有一个E值为j的i的排列,对于新加入的一个数i+1,将其加入排列的方法有三:1)把它 放最后,加入后E值不变    2)把它和一个满足A[k]>k的数交换,交换后E值不变       3)把它和一个不满足A[k]>k的数交换,交换后E值+1      根据这三种方法得到转移方程d

棋盘模型的状态压缩

此类状压,往往是在棋盘上放置互相影响的棋子(限制或不限制个数),求方案数 如: 在棋盘上放K个行不相邻的棋子 在棋盘上放K个列不相邻的棋子(其实完全一样) 在棋盘上放K个行列不相邻的棋子 在棋盘上的合法位置放K个行列不相邻的棋子 ...... 诸如此类 该模型的特点: 对每一个位置只有两种状态 每一个位置的状态只受相邻的有限个状态影响 宏观看每行的可能状态具有相似性 以"在棋盘上放K个行列不相邻的棋子"为例 可以用01表示每个位置的状态,但这样转移其来极其麻烦--对于(i,j)他需要从