动态规划总结【模板】

最长递增子序列

给定一个序列,找到最长子序列的长度,使得子序列中的所有元素被排序的顺序增加。

1.求最长递增子序列的长度O(N^2)

int Arr[30010],List[30010];
int LIS(int *Arr,int N)    //arr[]存放的是待求数组
{
    int Max = 0;        //max为最大递增子序列的长度
    for(int i = 1; i <= N; ++i)
        List[i] = 1;    //lis[i] 存放i之前的最大递增子序列的长度,初始都为1

    for(int i = 2; i <= N; ++i)
        for(int j = 1; j < i; ++j)    //遍历i之前所有位置
            if(Arr[i] >= Arr[j] && List[i]<List[j]+1)
                List[i] = List[j] + 1;
            //arr[i]>arr[j]为递增
            //lis[i]<lis[j] + 1确保为当前最长递增序列

    for(int i = 1; i <= N; ++i)
        if(Max < List[i])
            Max = List[i];

    return Max;
}

2.求最长递增子序列的长度O(NlogN)

int Arr[10010],List[10010];
int Stack[10010];
int LIS(int *Arr,int N)
{
    int top = 0;
    Stack[top] = -1;
    for(int i = 1; i <= N; ++i)
    {
        if(Arr[i] > Stack[top])
            Stack[++top] = Arr[i];
        else
        {
            int low = 1;
            int high = top;
            while(low <= high)
            {
                int mid = (low + high)/2;
                if(Arr[i] > Stack[mid])
                    low = mid + 1;
                else
                    high = mid - 1;
            }
            Stack[low] = Arr[i];
        }
    }
    return top;
}

最长公共子序列

给定两个序列,找出在两个序列中同时出现的最长子序列的长度。一个子序列是出现在相对顺序的序列,但不一定是连续的。

1.求最长公共子序列长度

char s1[220],s2[220];
int dp[220][220];
//求串s1和串s2的公共子序列
int lcs(char *s1,char *s2)
{
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    for(int i = 0; i <= len1; ++i)
    {
        for(int j = 0; j <= len2; ++j)
        {
            if(i == 0 || j == 0)
                dp[i][j] = 0;
            else if(s1[i-1] == s2[j-1])
                dp[i][j] = dp[i-1][j-1] + 1;
            else
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
        }
    }
    return dp[len1][len2];
}

2.求最长公共子序列长度,并输出路径

int dp[110][110],pre[110][110],len1,len2;
char s1[110],s2[110];

void LCS(char *s1,char *s2)
{
    for(int i = 0; i <= len1; ++i)
        pre[i][0] = 1;
    for(int i = 0; i <= len2; ++i)
        pre[0][i] = 2;
    //得到最长公共子序列,并标记dp[i][j]的上一个状态,用来回溯寻找路径
    for(int i = 0; i <= len1; ++i)
    {
        for(int j = 0; j <= len2; ++j)
        {
            if(i == 0 || j == 0)
                dp[i][j] = 0;
            if(s1[i-1] == s2[j-1])
            {
                dp[i][j] = dp[i-1][j-1] + 1;
                pre[i][j] = 0;
            }
            else if(dp[i-1][j] >= dp[i][j-1])
            {
                dp[i][j] = dp[i-1][j];
                pre[i][j] = 1;
            }
            else
            {
                dp[i][j] = dp[i][j-1];
                pre[i][j] = 2;
            }
        }
    }
}

void Print(int i,int j) //回溯输出新的字符串序列
{
    if(i == 0 && j == 0)
        return ;
    if(pre[i][j] == 0)
    {
        Print(i-1,j-1);
        printf("%c",s1[i-1]);
    }
    else if(pre[i][j] == 1)
    {
        Print(i-1,j);
        printf("%c",s1[i-1]);
    }
    else
    {
        Print(i,j-1);
        printf("%c",s2[j-1]);
    }
}

void solve(char *s1,char *s2)
{
    len1 = strlen(s1);
    len2 = strlen(s2);
    LCS(s1,s2);
    Print(len1,len2);
    printf("\n");
}

最长回文子序列

给一个字符串,找出它的最长的回文子序列LPS的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。

char s[2020];
int dp[2020][2020];
//dp[i][j]表示s[i~j]最长回文子序列
int LPS(char *s)
{
    memset(dp,0,sizeof(dp));
    int len = strlen(s);
    for(int i = len-1; i >= 0; --i)
    {
        dp[i][i] = 1;
        for(int j = i+1; j < len; ++j)
        {
            if(s[i] == s[j])    //头尾相同,最长回文子序列为去头尾的部分LPS加上头和尾
                dp[i][j] = dp[i+1][j-1] + 2;
            else    //头尾不同,最长回文子序列是去头部分的LPS和去尾部分LPS较长的
                dp[i][j] = max(dp[i][j-1],dp[i+1][j]);
        }
    }

    return dp[0][len-1];
}

最小编辑距离

给定一个长度为m和n的两个字符串,设有以下几种操作:替换(R),插入(I)和删除(D)且都是相同的操作。寻找到转换一个字符串插入到另一个需要修改的最小(操作)数量。

int dp[1010][1010],len1,len2;
char s1[1010],s2[1010];
int EditDist(char *s1,char *s2)
{
    int len1 = strlen(s1);
    int len2 = strlen(s2);
//当两个字符串的大小为0,其操作距离为0。
//当其中一个字符串的长度是零,需要的操作距离就是另一个字符串的长度.
    for(int i = 0; i <= len1; ++i)
        dp[i][0] = i;
    for(int i = 0; i <= len2; ++i)
        dp[0][i] = i;

    for(int i = 1; i <= len1; ++i)
    {
        for(int j = 1; j <= len2; ++j)
        {
            if(s1[i-1] == s2[j-1])  //对齐s1[i-1]和s2[j-1],不需改变
                dp[i][j] = dp[i-1][j-1];
            else
                dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1])) + 1;
            //s1前缀右对齐,s2前缀右为‘ ‘,删除s1第i个字符 -> dp[i-1][j]
            //s2前缀右对齐,s1前缀右为‘ ‘,删除s2第j个字符 -> dp[i][j-1]
            //s1前缀右对齐,s2前缀右对齐,i、j不一样,替换 -> dp[i-1][j-1]
        }
    }
    return dp[len1][len2];
}
时间: 2024-12-06 20:38:53

动态规划总结【模板】的相关文章

动态规划典型问题模板

动态规划典型问题模板 一.最长上升子序列(Longest increasing subsequence) 状态:以ak(k=1,2,3...N)为终点的最长递增子序列的长度. 状态转移方程: MaxLen(1) = 1 MaxLen(k) = Max{ MaxLen(i): 1<i<k 且 ai<ak 且 k≠1 } +1 这个转移方程的意思就是,MaxLen(k)的值,就是在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1.因为ak左边任何“终点”小于ak的子序列

【模板】LIS模板 洛谷P1091 [NOIP2004提高组]合唱队形 [2017年4月计划 动态规划11]

以题写模板. 写了两个:n^2版本与nlogn版本 P1091 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K). 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形. 输入输出格

动态规划模板1|LIS最长上升子序列

LIS最长上升子序列 dp[i]保存的是当前到下标为止的最长上升子序列的长度. 模板代码: int dp[MAX_N], a[MAX_N], n; int ans = 0; // 保存最大值 for (int i = 1; i <= n; ++i) { dp[i] = 1; for (int j = 1; j < i; ++j) { if (a[j] < a[i]) { dp[i] = max(dp[i], dp[j] + 1); } } ans = max(ans, dp[i]); }

模板 - 动态规划 - 数位dp

#include<bits/stdc++.h> using namespace std; #define ll long long int a[20]; ll dp[20][20/*可能需要的状态1*/][20/*可能需要的状态2*/];//不同题目状态不同 ll dfs(int pos,int state1/*可能需要的状态1*/,int state2/*可能需要的状态2*/,bool lead/*这一位的前面是否为零*/,bool limit/*这一位是否取值被限制(也就是上一位没有解除限

【luogu1439】 【模板】最长公共子序列 [动态规划][LIS最长上升子序列][离散化]

P1439 [模板]最长公共子序列 此思路详见luogu第一个题解 一个很妙的离散化 刘汝佳蓝书上面的LIS 详见蓝书 d[i]以i为结尾的最长上升子序列的长度     g[i]表示d值为i的最小状态的编号即长度为i的上升子序列的最小末尾值 1 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 2 for(int i=1;i<=n;++i) 3 { 4 int k=lower_bound(g+1,g+1+n,a[i])-g; 5 d[

动态规划四步解题法模板

导言 动态规划问题一直是算法面试当中的重点和难点,并且动态规划这种通过空间换取时间的算法思想在实际的工作中也会被频繁用到,这篇文章的目的主要是解释清楚 什么是动态规划,还有就是面对一道动态规划问题,一般的 思考步骤 以及其中的注意事项等等,最后通过几道题目将理论和实践结合. 什么是动态规划 如果你还没有听说过动态规划,或者仅仅只有耳闻,或许你可以看看 Quora 上面的这个 回答. 用一句话解释动态规划就是 “记住你之前做过的事”,如果更准确些,其实是 “记住你之前得到的答案”. 我举个大家工作

13动态规划模板

一.背包 1.01背包:hdu 2602 Bone Collector(01背包) 2.多重背包:hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包) 3.完全背包:hdu 4508 湫湫系列故事——减肥记I(完全背包)

【模板】求最长不下降序列 [动态规划 LIs]

求最长不下降序列 看不出来哪里还错了..... d[i]以i为结尾的最长上升子序列的长度     g[i]表示d值为i的最小状态的编号即长度为i的上升子序列的最小末尾值(d[j]=i的j值最小) liurujia's for(int i=1;i<=n;++i) g[i]=inf; for(int i=1;i<=n;++i){ int k=lower_buond(g+1,g+1+n,a[i])-g; d[i]=k; g[k]=a[i]; } 二昏好难啊..... 贴上90昏代码.... #inc

HNOI 2004 打砖块 动态规划

题意: 在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,--,最下面一层仅有一块砖.第i层的砖块从左至右编号为1,2,--,i,第i层的第j块砖有一个价值a[i,j](a[i,j]<=50).下面是一个有5层砖块的例子: 如果要敲掉第i层的第j块砖的话,若i=1,可以直接敲掉它,若i>1,则必须先敲掉第i-1层的第j和第j+1块砖. 你的任务是从一个有n(n<=50)层的砖块堆中,敲掉(m<=500)块砖,使得被敲掉的这些砖块的价值总和最大. 方法:动态规划 解