经典DP合集 ʕ •ᴥ•ʔ

1. 数字三角形/数塔问题(DP入门题)

        有形如下图所示的数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一起走到底层,要求找出一条路径,使路径上的值最大。

经典DP合集 ʕ •ᴥ•ʔ_第1张图片

 

样例输入:

5

13

11 8

12 7 26

6 14 15 8

12 7 13 24 11

样例输出:

86(13->8->26->15->24)

状态转移方程:dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j]; 从最低层往上搜索

#include 
#include 
#include 
#include 
#include 
#include 
//#include 
#define ll long long
using namespace std;
#define pai acos(-1,0)
int map[355][355];
int dp[355][355];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>map[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	dp[n][i]=map[n][i];
	for(int i=n-1;i>=1;i--)
	{
		for(int j=1;j<=i;j++)
		dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+map[i][j];
	}
	cout<

 

2. 序列DP

(1)最长上升子序列LIS

        输入n及一个长度为n的数列,求出此序列的最长上升子序列长度。上升子序列指的是对于任意的i

样例输入:

5

4 2 3 1 5

样例输出:

3(最长上升子序列为2, 3, 5)

#include 
#include 
#define maxn 1005
using namespace std;
int n,a[maxn];
int dp[maxn];    //dp[i]记录以a[i]为末尾的最长上升子序列的长度
int main()
{
	int i,j;
	int ret;
	while(scanf("%d",&n)!=EOF)
	{
	    for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            dp[i]=1;      //初始化
        }
        ret=1;
        for(i=1;i<=n;i++)
        {
            for(j=1;j

 

(2)最长公共子序列LCS

        给定两个字符串s1和s2(长度均不超过1000),求出这两个字符串的最长公共子序列的长度。

【分析】定义dp[i][j]:串s1的前i个字符 和 串s2的前j个字符的最长公共子序列长度,则s1…si+1和t1…tj+1对应的公共子列可能是:

        ①si+1=tj+1时:在s1…si 和 t1…tj的公共子列末尾追加si+1(即LCS长度+1)

        ②否则可能为s1…si和t1…tj+1的公共子列长度l1 或s1…si+1和t1…tj的公共子列长度l2,二者取较大者。

        故状态转移方程为:

        dp[i+1][j+1]=dp[i][j]+1,                                      

                                max(dp[i][j+1], dp[i+1][j]),        

        最后dp[len1][len2]即为所求,其中len1、len2分别为串s1和s2的长度。

#include 
#include 
#include 
using namespace std;
const int maxlen=1010;
char s1[maxlen],s2[maxlen];
int dp[maxlen][maxlen];   //dp[i][j]记录串s1的前i个字符和串s2的前j个字符的LCS长度
int main()
{
    int i,j;
    int len1,len2;
    while(scanf("%s",s1)!=EOF)
    {
        scanf("%s",s2);
        len1=strlen(s1);
        len2=strlen(s2);
        dp[0][0]=0;         //初始化:两串均为空时,len(LCS)=0
        for(i=1;i<=len1;i++)//s2串为空时,不论s1中有多少字符,len(LCS)=0
            dp[i][0]=0;
        for(i=1;i<=len2;i++)//s1串为空时,不论s1中有多少字符,len(LCS)=0
            dp[0][i]=0;
        for(i=0;i

 

(3)最大公共子串LCS

        给定两个字符串s1和s2(长度均不超过1000),求出这两个字符串的最大公共子串的长度。

【分析】情境类似求最长公共子序列长度问题,不过需要注意的是:所求子串中的字符需要在串s1和串s2中连续出现。

 

        例:s1=”abcad”

                s2=”abd”

        它们的最长公共子序列长度为3(”abd”),而最大公共子串长度为2(”ab”)。

        因此,定义dp[i][j]:串s1的前i个字符 和 串s2的前j个字符的最大公共子串长度,则s1…si+1和t1…tj+1对应的公共子串可能是:

        ①si+1=tj+1时:在s1…si 和 t1…tj的公共子串末尾追加si+1(即LCS长度+1)

        ②否则dp[i][j]=0

        分析可知状态转移方程:

        dp[i+1][j+1]=dp[i][j]+1,                     

                                0,                                   

 

#include 
#include 
#include 
using namespace std;
const int maxlen=1010;
char s1[maxlen],s2[maxlen];
int dp[maxlen][maxlen];    //dp[i][j]为串s1的前i个字符和串s2的前j个字符的最大公共子串长度
int main()
{
    int i,j;
    int len1,len2,ret;     //ret记录结果
    while(scanf("%s",s1)!=EOF)
    {
        scanf("%s",s2);
        memset(dp,0,sizeof(dp));   //初始化:开始LCS长度均为0
        len1=strlen(s1);
        len2=strlen(s2);
        ret=0;
        for(i=0;i

 

你可能感兴趣的:(经典DP合集 ʕ •ᴥ•ʔ)