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);
}
}