最大字段和 最大加权矩形 最长上升子序列(LIS)最长公共子序列(LCS)最大回文子串 Java实现

下面来写一下几种基础DP的问题,本人菜鸡,有什么不对欢迎指正

最大字段和

题目:试题OJ
在这里插入图片描述

这个题我们用一个dp数组 dp[i] : 以第i个数结尾的最大字段和的最大值
很明显 有转移方程: dp[i] = max(dp[i-1] + a[i] , a[i]) 答案就是max(dp[i])啦
代码:

package 前缀与差分;
import java.io.*;
import java.util.Arrays;
public class P1115dp {
	static int n;
	static int dp[]; //dp[i] 以第i个元素结尾的序列的最大值 所以 dp[i]=max(dp[i-1]+a[i],a[i]);
	static int a[];
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		re.nextToken(); n = (int)re.nval;
		a = new int[n+10];
		dp = new int[n+10];
		for(int i = 1;i <= n;i++){
			re.nextToken();
			a[i] = (int)re.nval;
		}
		int ans = -99999999;
		Arrays.fill(dp, -9999999); // 题目存在负数,所以我们初始化dp数组为 -INF
		for(int i = 1;i <= n;i++){
			dp[i] = Math.max(a[i],dp[i-1]+a[i]);
			ans = Math.max(ans, dp[i]);
		}
		pr.println(ans);
		pr.flush();
	}
}

最大加权矩形

题目: 试题OJ
在这里插入图片描述

最大加权矩形的模型其实就是最大字段和的一个变形,矩形要最大我们可以压缩这个矩形,得到一行数字,然后分别对这一行数字来求最大字段和即可,自己可以好好想想为什么矩形可以压缩成一行。
代码:

package 前缀与差分;
import java.io.*;
import java.util.Arrays;
public class P1719 {
	static int n;
	static int map[][] = new int[130][130];
	static int t[] = new int[130]; // 保存每次压缩的数列
	static int dp[] = new int[130];
	static int ans = 0;
	static void solve(){  // 子序列的和 用dp求   dp[i] 以i结尾最大子序列和
		Arrays.fill(dp, -999999);
		for(int i = 1;i <= n;i++){
			dp[i] = Math.max(dp[i-1]+t[i],t[i]);
			ans = Math.max(dp[i], ans);
		}
	}
	static void push(){ // 压缩
		for(int i = 1;i <= n;i++){
			Arrays.fill(t, 0);
			for(int j = i;j <= n;j++){
				for(int k = 1;k <= n;k++)
					t[k] += map[j][k];
				solve();
			}
		}
	}
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		re.nextToken(); n = (int)re.nval;
		for(int i = 1;i <= n;i++)
			for(int j = 1;j <= n;j++){
				re.nextToken();
				map[i][j] = (int)re.nval;
			}
		push();
		pr.println(ans);
		pr.flush();
	}
}

最长上升子序列(LIS)

题目:
在这里插入图片描述
第一种方法:dp O(n^2) 试题OJ
dp[i] : 数列以第i个数结尾的最长上升子序列的最大值
可以得到转移方程
dp[i] = max(dp[1] ~ dp[i-1])当然a[i]得大于这里面的值才行
dp 边界:每个子序列最小为1 也就是 dp 初始值为1
代码:

package LIS;
import java.io.*;
import java.util.Arrays;
public class P502竞码 {
	static int n;
	static int a[] = new int[1010];
	static int dp[] = new int[1010]; // dp[i]:以i为末尾最长上升子序列为多少  dp[i] = max(dp[1~i-1]+1) a[j]<a[i]
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		re.nextToken(); n = (int)re.nval;
		for(int i = 1;i <= n;i++){
			re.nextToken();
			a[i] = (int)re.nval;
		}
		Arrays.fill(dp,1);
		for(int i = 1;i <= n;i++) // 求出每一个的dp[i]
			for(int j = 1;j < i;j++)
				if(a[i] > a[j])
					dp[i] = Math.max(dp[j]+1,dp[i]);
		int ans = 0;
		for(int i = 1;i <= n;i++)
			ans = Math.max(ans, dp[i]);
		pr.println(ans);
		pr.flush();
	}
}

第二种方法 O(nlogn)
我们直接用一个数组(f [ ])来保存这个最长子序列长度,cnt 来表示这个数列的长度
我们每读入一个数,我们就判断这个数与 f[cnt] 的大小关系,如果比f[cnt]大,说明我们可以直接把这个数加入到数列最后面 即 f[++cnt] = a[i] ,如果小的话我们就把这个数边成数列中第一个大于他的数,具体为什么可以自己想想,模拟一下很快就懂啦,而我们的f数组是从小到大排序的 所以我们在找的时候可以使用二分,就可以达到 O(nlogn)的效果啦
这里有个题目 可以去试试,这个题是最长不下降子序列啊,大家要写的话加个等于就好啦 试题OJ
相对于这个试题的代码:

package 第四次模拟赛;
import java.io.*;
import java.util.*;
public class TestI {
	static int n;
	static int a[];
	static int b[];
	static int f[] = new int[100010];
	static int cnt = 0;
	static void bin(int x){
		int l = 0,r = cnt;
		int ans = 0;
		while(l <= r){
			int mid = (l+r)>>1;
			if(f[mid] > x){
				ans = mid;
				r = mid-1;
			}
			else
				l = mid+1;
		}
		f[ans] = x;
	}
	public static void main(String[] args) throws IOException{
		StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter pr = new PrintWriter(new OutputStreamWriter(System.out));
		re.nextToken(); n = (int)re.nval;
		a = new int[n<<1|1];
		b = new int[n<<1|1];
		for(int i = 1;i <= n*2;i++){
			re.nextToken();
			a[i] = (int)re.nval;
			b[i] = a[i];
		}
		Map<Integer,Integer> map = new HashMap<Integer,Integer>();
		Arrays.sort(b,1,2*n+1);
		for(int i = 1;i <= n;i++){
			map.put(b[i], i);
			map.put(b[2*n-i+1],i);
		}
		for(int i = 1;i <= n*2;i++)
			a[i] = map.get(a[i]);
	    //最长不下降子序列
		for(int i = 1;i < a.length;i++){
			if(a[i] >= f[cnt])
				f[++cnt] = a[i];
			else
				bin(a[i]);
		}
//		for(int i = 1;i <= cnt;i++)
//			pr.print(f[i]+" ");
		pr.println(2*n-cnt);
		pr.flush();
	}
}

最长公共子序列(LCS)

题目: 试题OJ
在这里插入图片描述

代码:
定义dp[i][j] : 一个串长度为i 一个串长度为j 他们的最长公共子序列长度
我们分为2种情况
(1)如果当前 x串第i个字符等于 y 串第 j个字符 这个时候 dp[i][j] = dp[i-1][j-1] +1; 即选这个数
(2)如果不相等的话,我们判断是不要x串的字符 好些 还是不要y串的字符好些
即 dp[i][j] = max(dp[i-1][j],dp[i][j-1])
我们从1开始循环 就可以保证 i-1 j -1 这些值都已经算过

import java.util.*;
public class Main {
	static char x[],y[];
	static int dp[][] = new int[5001][5001]; // dp[i][j] 一个长度为i 一个长度为j
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		x = sc.next().toCharArray();
		y = sc.next().toCharArray();
		for(int i = 1;i <= x.length;i++)
			for(int j = 1;j <= y.length;j++){
				if(x[i-1] == y[j-1]) // 读入数据时从0开始的 故这里-1
					dp[i][j] = dp[i-1][j-1]+1;
				else
					dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
			}
		System.out.println(dp[x.length][y.length]);
		sc.close();
	}
}

最大回文子串

题目:试题OJ
在这里插入图片描述
这个题正解区间DP
我们定义数组dp[i][j] : i ~ j 回文串的最长长度
我们枚举区间长度len,枚举起点 i,起点是i 那终点 j 自然就是 i+len-1
每一步我们可以选择的是
(1)选起点这个字符,也选终点这个字符(这就要保证这2个字符相同才能一起选)
(2)选起点这个字符,不选终点这个字符 dp[i[[j-1]
(3)不选起点这个字符,选终点这个字符 dp[i+1][j]
(4)不选起点这个字符,也不选终点这个字符 dp[i+1][j-1]

dp[i][j] 的取值就很明显了, 如果相等,就等于max(dp[i][j],dp[i+1][j-1]+2)
不相等的话 2 ,3 去最大即可
dp边界为 当长度为1的时候,我们的回文串长度也为1
代码: 相对于上面这个题的代码 ,如果直接求回文串的话 dp[0][a.length-1] 就已经是答案了

package lan7A;
import java.util.*;
public class P7008 {
	static int N = 1010;
	static int dp[][] = new int[N][N];// dp[i][j] i~j 最大回文子串长度
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		char a[] = sc.next().toCharArray();
		for(int len = 1;len <= a.length;len++){ // 枚举区间长度
			for(int i = 0;i+len-1 < a.length;i++){ // 枚举区间起点不能超过总长
				int j = i+len-1; // 区间终点
				if(len == 1) dp[i][j] = 1; // 长度为1 回文串都是1
				else{
					dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);
					if(a[i] == a[j])
						dp[i][j] = Math.max(dp[i][j],dp[i+1][j-1]+2);
				}
			}
		}
		System.out.println(a.length - dp[0][a.length-1]);
		sc.close();
	}
}

有问题欢迎指正,本弱鸡dp这方面菜的不行,,,

发布了32 篇原创文章 · 获赞 5 · 访问量 862

猜你喜欢

转载自blog.csdn.net/shizhuba/article/details/105301913