第3章 动态规划(二)【DP序列问题】
3.2 DP序列问题
(51nod的动态规划教程很不错,讲解很详细,以下分析来自51nod)
1.矩阵取数问题
给定一个m行n列的矩阵,矩阵每个元素是一个正整数,你现在在左上角(第一行第一列),你需要走到右下角(第m行,第n列),每次只能朝右或者下走到相邻的位置,不能走出矩阵。走过的数的总和作为你的得分,求最大的得分。
分析:
从起点到终点的最优路径上经过了(m + n – 1)个点,则这条路径上对应起点到这(m + n – 1)个点的子路径也都从起点到该位置的所有路径中和最大的路径。
定义f(int x,int y)表示从起点到第x行第y列的最优路径上的数之和,有从起点达到x,y的最优路径有两种可能:
要么f(x – 1, y) + A[x][y]
要么f(x, y – 1) + A[x][y]
所以,f(x, y) = max(f(x – 1, y) , f(x, y – 1) ) + A[x][y]
初值:
f(1,1)是起点,没的选f(1,1) = A[1][1]。
按照递推式 f(1,2) = max(f(0, y) , f(1,1)) + A[1][2],考虑实际意义,这表示要么从上面到达(1,2)要么从左面到达(1,2),可是上面没有位置,说明没的选。所以可以定义f(0, y) = -∞, 同理f(x, 0) = -∞。
递推式:
输入
第1行:N,N为矩阵的大小。(2 <= N <= 500)
第2 - N + 1行:每行N个数,中间用空格隔开,对应格子中奖励的价值。(1 <= N[i] <= 10000)
输出
输出能够获得的最大价值。
输入示例
3
1 3 3
2 1 3
2 2 1
输出示例
11
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
int n,i,j;
int a[505][505];
while(cin>>n)
{
for(i=0; i<n; i++)
{
a[0][i]=0;
a[i][0]=0;
}
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
cin>>a[i][j];
}
}
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
a[i][j]=max(a[i-1][j],a[i][j-1])+a[i][j];
}
}
cout<<a[n][n]<<endl;
}
return 0;
}
2.编辑距离问题
给定两个字符串S和T,对于T我们允许三种操作:
(1) 在任意位置添加任意字符
(2) 删除存在的任意字符
(3) 修改任意字符
问最少操作多少次可以把字符串T变成S?
例如: S= “ABCF” T = “DBFG”
那么可以
把D改为A
(2) 删掉G
(3) 加入C
所以答案是3。
分析:
给定字符串S和T,可以用一种特殊字符促成两个字符串的对齐。特殊字符是“-”, 允许在S和T中任意添加这种特殊字符使得它长度相同,然后让这两个串“对齐”,最终两个串相同位置出现了不同字符,就扣1分,要使得这两个串对齐扣分尽量少。
对于例子,采取这样的对齐方式:
12345
ABCF-
DB-FG
注意:如果要对齐,两个“-”相对是没有意义的,所以要求不出现这种情况。
那么:
(1) S,T对应位置都是普通字符,相同,则不扣分。 例如位置2,4
(2) S,T对应位置都是普通字符,不同,则扣1分。 例如位置1
(3) S在该位置是特殊字符,T在该位置是普通字符,则扣1分,例如位置5
(4) S在该位置是普通字符,T在该位置是特殊字符,则扣1分,例如位置3
扣分项目对应:
(1) 不扣分,直接对应
(2) 对应把T中对应位置的字符修改
(3) 对应在T中删除该字符
(4) 对应在T中添加该字符
设f(i,j)表示S的前i位和T的前j位对齐后的最少扣分。
最后一位对齐的情况:
(1) S[i] == T[j], 这时前i – 1和j – 1位都已经对齐了,这部分肯定要最少扣分。这种情况下最少的扣分是f(i-1,j-1)
(2) S[i]≠T[j],这种情况下最少的扣分是f(i -1, j – 1) + 1
(3) S的前i位和T的前(j – 1)位已经对齐了,这部分扣分也要最少。这种情况下最少的扣分是f(i,j-1) + 1
(4) S的前(i-1)位已经和T的前j位对齐了,这部分扣分要最少。这种情况下最少的扣分是f(i,j-1) + 1
具体f(i,j)取什么值,要看哪种情况的扣分最少。
递推式:
f(i,j) = min(f(i – 1, j – 1) + same(i,j), f(i – 1,j ) + 1, f(i, j – 1) + 1)
初值:
因为对于S的前0位,我们只能在之前加入“-”,或者说把T全部删掉了。类似地,对于T地前0位,我们只能把S的字符都加进来,别无选择。
f(0, j) = j
f(i, 0) = i
输入
第1行:字符串a(a的长度 <= 1000)。
第2行:字符串b(b的长度 <= 1000)。
输出
输出a和b的编辑距离
输入示例
kitten
sitting
输出示例
3
代码:
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
char str1[1005],str2[1005