动态规划各种模型整合

<<DP问题各种模型的状态转移方程>>

1(最长公共子序列)

两个字符串str1str2,长度分别为(l1,l2)

dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度

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

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

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

dp[i][j]=0;

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



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

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

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

核心代码:

fori=1i<=n;i++){

dp[i]=1;

fork=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如果有正数

fori=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]即为最大值



501背包问题)

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]};

核心代码:

fori=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]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。


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

这时候我们可以像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]};

核心代码:

fori=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])
}

}

}



  1. 混合三种背包问题

fori=1..N

ifi件物品属于01背包

ZeroOnePack(c[i],w[i])

elseifi件物品属于完全背包

CompletePack(c[i],w[i])

elseifi件物品属于多重背包

MultiplePack(c[i],w[i],n[i])

  1. 二维费用的背包问题)

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

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

状态转移方程就是:

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



10(分组的背包问题)

问题

N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:

f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}

使用一维数组的伪代码如下:

for所有的组k

forv=V..0

for所有的i属于组k

f[v]=max{f[v],f[v-c[i]]+w[i]}



注意这里的三层循环的顺序,甚至在本文的第一个beta版中我自己都写错了。forv=V..0”这一层循环必须在for所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

另外,显然可以对每组内的物品应用P02一个简单有效的优化



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

给定一个序列为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).

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

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



4矩阵连乘

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

思路:建立递推表达式,利用动态规划的方式

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,ij之间游荡,所以i<=k<j;

因为你要保证在计算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]即为最终求解

模板代码(代码实现时需要注意的问题:计算顺序!!!)

#include<iostream>

usingnamespace std;

constint MAX = 100;

//p用来记录矩阵的行列,main函数中有说明

//m[i][j]用来记录第i个矩阵至第j个矩阵的最优解

//s[][]用来记录从哪里断开的才可得到该最优解

intp[MAX+1],m[MAX][MAX],s[MAX][MAX];

intn;//矩阵个数

voidmatrixChain(){

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

m[i][i]=0;//相同初始化为0

for(intr=2;r<=n;r++)//对角线循环

for(inti=1;i<=n-r+1;i++){//行循环

intj = r+i-1;//列的控制

//m[i][j]的最小值,先初始化一下,令k=i

m[i][j]=m[i][i]+m[i+1][j]+p[i-1]*p[i]*p[j];

s[i][j]=i;

//ki+1j-1循环找m[i][j]的最小值

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

inttemp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];

if(temp<m[i][j]){

m[i][j]=temp;//更新

//s[][]用来记录在子序列i-j段中,在k位置处

//断开能得到最优解

s[i][j]=k;

}

}

}

}

//根据s[][]记录的各个子段的最优解,将其输出

voidtraceback(int i,int j){

if(i==j)

return;

traceback(i,s[i][j]);

traceback(s[i][j]+1,j);

cout<<"MultiplyA"<<i<<","<<s[i][j]<<"andA"<<s[i][j]+1<<","<<j<<endl;

}

intmain(){

cin>>n;

for(inti=0;i<=n;i++)

cin>>p[i];

//测试数据可以设为六个矩阵分别为

//A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]

//p[0-6]={30,35,15,5,10,20,25}

//输入:630 35 15 5 10 20 25

matrixChain();

traceback(1,n);

//最终解值为m[1][n];

cout<<m[1][n]<<endl;

return0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值