2024-02-11 ~ 2024-02-12(最长上升子序列模型)

2024-02-06(线性DP、区间DP)-CSDN博客 

 

895. 最长上升子序列 - AcWing题库

(考虑的是上升的最大值) 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int N = 1010;
        int[] a = new int[N];
        int[] f = new int[N];
        
        for(int i = 1; i <= n; i ++){
            a[i] = sc.nextInt();
        }
        
        for(int i = 1; i <= n; i ++){
            f[i] = 1;
            for(int j = 1; j < i; j ++){
                if(a[i] > a[j]) f[i] = Math.max(f[i], f[j] + 1);
            }
        }
        
        int res = -0x3f3f3f3f;
        for(int i = 1; i <= n; i ++){
            res = Math.max(res, f[i]);
        }
        System.out.print(res);
    }
}

 

 1017. 怪盗基德的滑翔翼 - AcWing题库

(考虑的是上升和下降的最大值) 

        这道题和最长上升子序列的基本模型的一点区别就是:它要考虑最长上升子序列和最长下降子序列。 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        
        while(k -- > 0){
            int n = sc.nextInt();
            int N = 110;
            int res = 0;
            int[] a = new int[N];
            int[] f = new int[N];
            int[] d = new int[N];
            
            for(int i = 1; i <= n; i ++){
                a[i] = sc.nextInt();
            }
            
            //正向求解
            for(int i = 1; i <= n; i ++){
                f[i] = 1; 
                for(int j = 1; j < i; j ++){
                    if(a[i] > a[j]) f[i] = Math.max(f[i], f[j] + 1);
                }
                res = Math.max(res, f[i]);
            }
            
            //逆向求解
            for(int i = n; i >= 1; i --){
                d[i] = 1;
                for(int j = n; j > i; j --){
                    if(a[i] > a[j]) d[i] = Math.max(d[i], d[j] + 1);
                }
                res = Math.max(res, d[i]);
            }
            
            System.out.println(res);
        }
    }
}

 

1014. 登山 - AcWing题库

(考虑的是上升和下降的和的最大值) 

条件一:按照编号递增的顺序来浏览(也就是说必须是子序列)

条件二:相邻的两个景点不能重复浏览

条件三:一旦开始下降,就不能上升了

目标:求最多能浏览多少景点? 

求出所有长度是下面这种子序列长度的最大值

 ​​​

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 1010;
        int[] a = new int[N];
        int[] f = new int[N];
        int[] g = new int[N];
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++){
            a[i] = sc.nextInt();
        }
        
        //上升序列
        for(int i = 1; i <= n; i ++){
            f[i] = 1;
            for(int j = 1; j < i; j ++){
                if(a[i] > a[j]) f[i] = Math.max(f[i], f[j] + 1);
            }
        }
        //下降序列
        for(int i = n; i >= 1; i --){
            g[i] = 1;
            for(int j = n; j > i; j --){
                if(a[i] > a[j]) g[i] = Math.max(g[i], g[j] + 1);
            }
        }
        
        //相加得结果
        int res = 0;
        for(int i = 1; i <= n; i ++){
            res = Math.max(res, f[i] + g[i] - 1);
        }
        System.out.print(res);
    }
}

 

482. 合唱队形 - AcWing题库

唯一的区别就是要求算出子序列以外的部分,本质上和登山是一样的 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 110;
        int n = sc.nextInt();
        int[] a = new int[N];
        int[] f = new int[N];
        int[] g = new int[N];
        
        for(int i = 1; i <= n; i ++){
            a[i] = sc.nextInt();
        }
        
        for(int i = 1; i <= n; i ++){
            f[i] = 1;
            for(int j = 1; j < i; j ++){
                if(a[i] > a[j]) f[i] = Math.max(f[i], f[j] + 1);
            }
        }
        
        for(int i = n; i >= 1; i --){
            g[i] = 1;
            for(int j = n; j > i; j --){
                if(a[i] > a[j]) g[i] = Math.max(g[i], g[j] + 1);
            }
        }
        
        int res = 0;
        for(int i = 1; i <= n; i ++){
            res = Math.max(res, f[i] + g[i] - 1);
        }
        System.out.print(n - res);//唯一的区别就是这里
    }
}

 

1012. 友好城市 - AcWing题库

这个转化的过程很重要!!!! 

 

按照自变量的大小给因变量排序,然后再按照最长上升子序列来做。

import java.util.*;

class PII implements Comparable<PII>{
    int x, y;
    public PII(int x, int y){
        this.x = x;
        this.y = y;
    }
    public int compareTo(PII o){
        return Integer.compare(y, o.y);
    }
}

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int N = 5010;
        PII[] a = new PII[N * N];
        int[] f = new int[N];
        
        for(int i = 1; i <= n; i ++){
            int x = sc.nextInt();
            int y = sc.nextInt();
            a[i] = new PII(x, y);
        }
        
        Arrays.sort(a, 1, n + 1);//根据自变量的大小给因变量排序
        
        //以下是最长上升子序列的模板
        int res = 0;
        for(int i = 1; i <= n; i ++){
            f[i] = 1;
            for(int j = 1; j < i; j ++){
                if(a[i].x > a[j].x) f[i] = Math.max(f[i], f[j] + 1);
            }
            res = Math.max(res, f[i]);
        }
        System.out.print(res);
    }
}

 

1016. 最大上升子序列和 - AcWing题库

注意最长上升子序列和最大上升子序列的不同之处!!! 

 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 1010;
        int[] a = new int[N];
        int[] f = new int[N];
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++){
            a[i] = sc.nextInt();
        }
        
        int res = 0;
        for(int i = 1; i <= n; i ++){
            //最长上升子序列这里的初始值是1,最大上升子序列是a[i]
            f[i] = a[i];
            for(int j = 1; j < i; j ++){
                //最长上升子序列这里是加1,最大上升子序列是加a[i]
                if(a[i] > a[j]) f[i] = Math.max(f[i], f[j] + a[i]);
            }
            res = Math.max(res, f[i]);
        }
        
        System.out.print(res);
    }
}

1010. 拦截导弹 - AcWing题库

对于每个数都有两种选择:1.接在某个现有的子序列的后面 2.创建一个新的系统 

要想使得系统的数量最小,那么子序列结尾的数就要尽可能大

2024-02-06(线性DP、区间DP)-CSDN博客中的最长子序列2的贪心比较相似

贪心流程:

        从前往后扫描每个数,对于每个数:

        情况一:现有子序列的结尾数都小于当前数,则创建一个新的子序列

        情况二:将当前数放到结尾数大于等于它的最小的子序列的后面

Java Scanner的hasNext()方法 - realzhangsan - 博客园 (cnblogs.com) 

方法一:最长上升子序列1+2 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 1010;
        int n = 0;
        int[] a = new int[N];
        int[] f = new int[N];
        int[] q = new int[N];
        
        while(sc.hasNext()){
            a[n ++] = sc.nextInt();
        }
        
        //最长上升子序列1的模板
        int res = 0;
        for(int i = 0; i < n; i ++){
            f[i] = 1;
            for(int j = 0; j < i; j ++){
                //由于是下降,所以是a[i] <= a[j]
                if(a[i] <= a[j]) f[i] = Math.max(f[i], f[j] + 1);
            }
            res = Math.max(res, f[i]);
        }
        System.out.println(res);
        
        //最长上升子序列2的模板
        int cnt = 0;
        for(int i = 0; i < n; i ++){
            int l = 0;
            int r = cnt;
            while(l < r){
                int mid = l + r + 1 >> 1;
                if(q[mid] < a[i]) l = mid;
                else r = mid - 1;
            }
            cnt = Math.max(cnt, r + 1);
            q[r + 1] = a[i];
        }
        System.out.print(cnt);
    }
}

 方法二:最长上升子序列1+不上升子序列

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 1010;
        int n = 0;
        int[] a = new int[N];
        int[] f = new int[N];
        int[] q = new int[N];
        
        while(sc.hasNext()){
            a[n ++] = sc.nextInt();
        }
        
        //最长上升子序列1的模板
        int res = 0;
        for(int i = 0; i < n; i ++){
            f[i] = 1;
            for(int j = 0; j < i; j ++){
                //由于是下降,所以是a[i] <= a[j]
                if(a[i] <= a[j]) f[i] = Math.max(f[i], f[j] + 1);
            }
            res = Math.max(res, f[i]);
        }
        System.out.println(res);
        
        //不上升子序列
        int len = 0;//表示可以覆盖全部输入的不上升子序列的数量
        for(int i = 0; i < n; i ++){
            //从前往后枚举所有子序列(结尾数),一定是单调上升的
            //只要枚举到第一个比自己大的数,就可以直接替换掉
            int k = 0;
            while(k < len && q[k] < a[i]) k ++;
            //将第k个序列的结尾数换成当前数
            q[k] = a[i];
            if(len <= k) len ++;//如果枚举完全部序列,都没有找到,那么就要新开一个
        }
        System.out.print(len);
    }
}

187. 导弹防御系统 - AcWing题库

由于没有办法确定放在上升还是下降,所以用暴力搜索来遍历。 

import java.util.*;

public class Main{
    static int N = 55;
    static int n, ans;
    static int[] a = new int[N];
    static int[] f = new int[N];
    static int[] up = new int[N];
    static int[] down = new int[N];
    
    public static void dfs(int u, int st, int sd){
        if(st + sd >= ans) return;
        if(u == n){
            ans = st + sd;
            return;
        }
        
        //将当前数据放到上升子序列中
        int k = 0;//表示第几个上升子序列
        while(k < st && up[k] >= a[u]) k ++;//直到找到结尾数比它小的(结尾数的排列是单调递减的)
        int t = up[k];
        up[k] = a[u];
        if(k < st) dfs(u + 1, st, sd);
        else dfs(u + 1, st + 1, sd);//否则新开一个
        up[k] = t;//回溯
        
        //将当前数据放到下降子序列中
        k = 0;//表示第几个下降子序列
        while(k < sd && down[k] <= a[u]) k ++;//直到找到结尾数比它大的(结尾数的排列是单调递增的)
        t = down[k];
        down[k] = a[u];
        if(k < sd) dfs(u + 1, st, sd);
        else dfs(u + 1, st, sd + 1);//否则新开一个
        down[k] = t;//回溯
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while(true){
            n = sc.nextInt();
            if(n == 0) break;
            for(int i = 0; i < n; i ++) a[i] = sc.nextInt();
            ans = n;//定义一个全局变量表示答案
            dfs(0, 0, 0);//从第0个数开始,有0个上升子序列,0个下降子序列
            System.out.println(ans);
        }
    }
}

 272. 最长公共上升子序列 - AcWing 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = 3010;
        int[] a = new int[N];
        int[] b = new int[N];
        int[][] f = new int[N][N];
        
        int n = sc.nextInt();
        for(int i = 1; i <= n; i ++) a[i] = sc.nextInt();
        for(int i = 1; i <= n; i ++) b[i] = sc.nextInt();
        
        for(int i = 1; i <= n; i ++){
            int maxv = 1;//表示1到j-1(在满足bk < bj的最大值
            for(int j = 1; j <= n; j ++){
                f[i][j] = f[i - 1][j];
                if(a[i] == b[j]){
                    f[i][j] = Math.max(f[i][j], maxv);
                }
                if(b[j] < a[i]) maxv = Math.max(maxv, f[i][j] + 1);
            }
        }
        
        int res = 0;
        for(int i = 1; i <= n; i ++){
            res = Math.max(res, f[n][i]);
        }
        System.out.print(res);
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值