【动态规划】线性DP

137 阅读2分钟

数字三角形

从顶层走到最底层,每一步可以选择走左下角或者右下角,选择一条路径上数字和最大的路径 image.png

  • 状态表示
    • 集合:所有从起点,走到(i,j)的路径
    • 属性:Max
  • 状态计算

image.png

let buf=''
process.stdin.on('readable',function(){
    let chunk = process.stdin.read();
    if(chunk) buf+=chunk.toString();
})
process.stdin.on('end',function(){
    //处理输入三角形
    let inputs=buf.split('\n');
    let n=parseInt(inputs.shift());
    let a = [];
    for(let i=0;i<=n;i++){
        a[i]=new Array(i);
    }
    a[1][1]=parseInt(inputs.shift());
    for(let i=2;i<=n;i++){
        let line=inputs.shift().split(" ")
        for(let j=1;j<=i;j++){
            a[i][j]=parseInt(line.shift());
        }
    }
    //初始化DP
    let f=[]
    for(let i=0;i<=n;i++){
        f[i]=new Array(i+2).fill(-Infinity);
    }
    f[1][1]=a[1][1];
    for(let i=2;i<=n;i++){
        for(j=1;j<=i;j++){
            f[i][j]=Math.max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
        }
    }
    let res = -Infinity;
    for(let i=1;i<=n;i++){
        res = Math.max(res,f[n][i]);
    }
    console.log(res)
})

最长上升子序列

找到给定整数数组中最长严格递增子序列的长度

  • 状态表示
    • 集合: 所有以第i个数结尾的上升子序列
    • 属性: max
  • 状态计算 image.png
var lengthOfLIS = function(nums) {
    const len = nums.length;
    if(!len) return 0;
    let maxLen = 1;
    let dp = new Array(len).fill(1)
    for(let i=1;i<len;i++){
        for(let j=0;j<i;j++){
            //如果发现比当前元素小的元素,则为可以延长的上升子序列,更新状态
            if(nums[j]<nums[i]){
                dp[i]=Math.max(dp[i],dp[j]+1);
            }
        }
        //更新最大值
        if(dp[i]>maxLen) maxLen=dp[i];
    }
    return maxLen;
};

最长公共子序列

给定两个字符串 返回这两个字符串的最长公共子序列的长度

  • 状态表示f[i][j]
    • 集合:所有a[1-i]、b[1-j]的公共子序列的集合
    • 属性: max
  • 状态计算

image.png

00对应的a[i]和b[i]都不包含的情况其实在01或者10种都有可能出现,重复了所以不用划分这种情况,只有三种情况。

var longestCommonSubsequence = function(text1, text2) {
    let n=text1.length;
    let m=text2.length;
    let arr1=new Array(0,...text1);
    let arr2=new Array(0,...text2);
    let f=[];
    for(let i=0;i<=n;i++){
        f[i] = new Int32Array(m+1);
    }
    for(let i=1;i<=n;i++){
        for(let j=1;j<=m;j++){
            f[i][j] = Math.max(f[i-1][j],f[i][j-1]);
            if(arr1[i]===arr2[j]) f[i][j]= Math.max(f[i][j],f[i-1][j-1]+1);
        }
    }
    return f[n][m];
};

最短编辑距离

字符串1转换成字符串需要的最少的操作步骤数 操作:增1字符 删1字符 改1个字符

  • 状态表示
    • 集合:将a[i-i]转换成b[1-j]的所有操作方式
    • 属性:最少的步骤 min
  • 状态计算 image.png

最后一种情况,如果a[i]和b[j]不相等 需要一步;相等就不需要修改

let buf = "";
process.stdin.on('readable',function(){
    let chunk = process.stdin.read();
    if(chunk) buf+=chunk.toString();
})

process.stdin.on('end',function(){
    const lines = buf.split("\n");
    const n = parseInt(lines[0]);
    const m=parseInt(lines[2])
    const a=new Array(0,...lines[1]);
    const b=new Array(0,...lines[3]);
    
    let f=[];
    for(let i=0;i<=n;i++){
        f[i]=new Int32Array(m+1);
    }
    
    for(let i=1;i<=n;i++) f[i][0]=i;
    for(let i =1;i<=m;i++) f[0][i]=i;
    
    for(let i=1;i<=n;i++){
        for(let j=1;j<=m;j++){
            f[i][j]=Math.min(f[i-1][j]+1,f[i][j-1]+1);
            if(a[i]===b[j]){
                f[i][j]=Math.min(f[i][j],f[i-1][j-1]);
            }else{
                f[i][j]=Math.min(f[i][j],f[i-1][j-1]+1);
            }
        }
    }
    console.log(f[n][m])
})