一.线性DP的概念
线性dp是很基础的一种动态规划,经典题和他的变种有很多(我还是比较菜,先理解好简单的吧…)比如两个串的LCS,LIS,最大子序列和等等,线性dp是用来解决一些 线性区间上的最优化问题 .
二.线性DP举例
(1)最长公共子序列
描述
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
输入
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.
输出
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
大意
给出两个字符串,求两个字符串的最长公共字串。
分析
这种题还是用图来表示比较清晰明了(上图!我还是不太会调大小…各位先凑活看吧)
我们就可以推出这么一个式子:
如果a[i]=b[j];
F[i][j]=F[i-1][j-1]+1;
如果a[i]!=b[j];
F[i][j]=max(F[i-1][j],F[i][j-1]);
那我们话不多说,(结合各路大神思路)直接上代码:
#include<stdio.h>
#include<string.h>
int f[1001][1001];
int main()
{
char a[1001],b[1001];
int i,j,len1,len2;
while(~scanf("%s %s",a,b))
{
len1=strlen(a);
len2=strlen(b);
for(i=0;i<=len1;i++)
{
f[i][0]=0;
}
for(i=0;i<=len2;i++)
{
f[0][i]=0;
}
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(a[i-1]==b[j-1])
{
f[i][j]=f[i-1][j-1]+1;
}
else
{
f[i][j]=f[i-1][j]>f[i][j-1]?f[i-1][j]:f[i][j-1];
}
}
}
printf("%d\n",f[len1][len2]);
}
system("pause");
return 0;
}
(2)最长上升子序列
描述
A numeric sequence of ai is ordered if a1 < a2 < … < aN. Let the subsequence of the given numeric sequence (a1, a2, …, aN) be any sequence (ai1, ai2, …, aiK), where 1 <= i1 < i2 < … < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
输入
The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000
输出
Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.
分析
我们来简单描述一下步骤
ep:
a[i]:用此来存储前第i个数的最长子序列
{1}--------------------------a[1]=1表示最长子序列是{1}
{1,6}-----------------------a[2]=a[1]+1=2表示前两个数里最长上升子序列为{1,6}
{1,6,4}---------------------a[3]=a[1]+1=2由于4<6,所以是a[1]+1。{1,6}{1,4}
{1,6,4,2}------------------a[4]=a[1]+1=2同理上一步,仍是{1,6}{1,4}
{1,6,4,2,3}----------------a[5]=a[4]+1=3这里3>2,为{1,2,3}
{1,6,4,2,3,9}-------------a[6]=a[5]+1=4按这个推理,这里为{1,2,3,9}
{1,6,4,2,3,9,8}-----------a[7]=4这里可以看出,这里为{1,2,3,9}{1,2,3,8}
理解了上面的过程,就可以上代码了
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n;
while(cin>>n)
{
int a[1001]={0},m=-1,d[1001]={0};// d用于保存第i个数字时的最长子序列 m用于保存d[i]数组中的最长子序列的个数
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
d[1]=1;
if(n<2)
cout<<d[1]<<endl;//数组长度为1的时候,最长子序列也是1
else
{
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[j]<a[i]) // 如果比第j个数比第i个数小,递增
d[i] = max(d[i],d[j]); //第i个数之前的最长上升子序列
}
d[i]=d[i]+1;// 在第i个之前加上自身 等于第i个数的最长上升子序列
m = max(d[i],m);
}
}
system("pause");
cout<<m<<endl;
}
return 0;
}
线性dp基本上是从这两类出发,然后再进一步延申,背包问题好像也可用线性dp做,但我感觉和贪心的思路相差不大,也就不做详解了。
三.总结
这周又深入学习了动态规划——线性DP,然而自己还是菜,这里只能简单说一下对线性DP的基本例子,对一些变种例题,我怕是还不能叙述清楚明白,我还需要不断做题理解,这里只是我粗略的理解,如有不对之处,还烦请各路大神指出!