线性DP、区间DP

线形DP

AcWing 898. 数字三角形

将想要计算的点的状态分为两部分,从左上角、右上角两部分走过来。求最大值:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 510, INF= 1e9;
int a[N][N], f[N][N];
int n;

int main(){
	cin >> n;
	memset(f, -0x3f, sizeof f);
	
	for(int i = 1; i<=n; i++){
		for(int j = 1; j<=i; j++) cin >> a[i][j];
	}
	
	f[0][0] = 0;
	for(int i = 1; i<=n; i++){
		for(int j = 1; j<=i; j++){
			f[i][j] = max(f[i-1][j-1]+a[i][j], f[i-1][j]+a[i][j]);
		}
	}
	
	int res = -INF;
	for(int i =1 ; i<=n; i++){
//		cout << "------1" << endl;
		res = max(res, f[n][i]); 
	}
	
	cout << res;
	return 0;
} 

AcWing 895. 最长上升子序列

需要计算点是由什么状态到达的, 该点的前面状态是小于这个点的位置。可以用数组g来记录路径,fi = fj + 1; gi = j; 存储该节点的上一个节点坐标

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N= 1010;
int a[N], f[N], g[N];
int n;

int main(){
	cin >> n;
	for(int i = 1; i<=n; i++) cin >> a[i];
	
	for(int i = 1; i<=n; i++){
		f[i] = 1;//最小的长度为1; 
		g[i] = 0;
		for(int j = 1; j<i; j++){
			if(a[j] < a[i]){
				if(f[i] < f[j]+1){
					f[i] = f[j] + 1;
					g[i] = j;
				}

			}
		}
	}
	int res = 0;
	int k = 0;
	for(int i =1; i<=n; i++)  
	{
		res = max(res, f[i]);
		k = i;
	}

	
	cout << res << endl;
	
	for(int i = 0, len = f[k]; i<len; i++){
		cout << a[k] << " ";
		k = g[k];
	}
	return 0;
}

896. 最长上升子序列 II

用p 数组来存储结果,遍历输入的数组与p数组的结尾比较大的就进入,当ai <= fcnt 的时,我们就用ai去替代数组 p 中的第一个**大于等于ai**的数

我们用ai去替代fi的含义是:以ai为最后一个数的严格单调递增序列,这个序列中数的个数为l个。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
int a[N], q[N];
int n;
int cnt;
int find(int x){
	int l = 0, r= cnt;
	while(l < r){
		int mid = l + r >> 1;
		if(q[mid] >= x) r = mid;
		else l = mid + 1;
	} 
	
	return r;
}

int main(){
	cin >> n;
	for(int i = 1; i<=n; i++) cin >> a[i];
	
	memset(q, -0x3f, sizeof q);

	for(int i = 1; i<=n; i++){
		if(a[i] > q[cnt]){
			q[++cnt] = a[i];
		}else {
			q[find(a[i])] = a[i];
		}
	}
	cout << cnt;	
	return 0;
}
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int a[N], q[N];
int n;

int main(){
    cin >> n ;
    
    for(int i =1; i<=n; i++) cin >> a[i];
    
    int len = 0;
    q[0] = -2e9;
    for(int i = 1; i<=n; i++){
        int l = 0, r = len ;
        while(l < r){
            int mid = l + r + 1 >> 1;

            if(q[mid] < a[i]) l = mid;
            else r = mid-1;
        }
        
        len = max(len, l+1);
        q[l+1] = a[i];  
    }
    
    cout << len;
    return 0;
}

897. 最长公共子序列

状态转移:f i j 是由当前位置的字符是否相等来判断的, 如果相等就可以直接转移到 fi-1j-1 +1。不相等的话,可以对**fi-1j (不包含ai的最大值),fij-1**两种状态取max来转移。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
char a[N], b[N];
int f[N][N];
int n, m;

int main(){
	cin >> n >> m;
	cin >> a+1 >> b+1;
	
	for(int i = 1; i<=n; i++){
		for(int j = 1; j<=m; j++){
			f[i][j] = max(f[i-1][j], f[i][j-1]);//这种一定小于f[i-1][j-1] + 1可以先求出
			if(a[i] == b[j]) f[i][j] = max(f[i-1][j-1] + 1, f[i][j]);
		}
	}
	
	cout << f[n][m];
	return 0;
}

902. 最短编辑距离

状态方程:fij代表前 a的前i个字符和b的前j个字符相同最少要操作多少次

f i j =min({

// f i - 1 j + 1, // 删除 ai删之前i-1个 = j个

// f i j-1 + 1, // 在 ai 后插入 bj添加之前前i个等于j-1个

// f i-1 j-1 + (a i != b j ) // 替换:不同就+1,相同就+0

// });

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
char a[N], b[N];
int f[N][N];
int n, m;

int main(){
	cin >> n >> a+1;
	cin >> m >> b+1;
//	//f[i][j] = min(f[i][j], f[i-1][j-1]);最小值不能直接先比 
//	memset(f, 0x3f, sizeof f); 
	//f[i][j]代表前 a的前i个字符和b的前j个字符最少要操作多少次 
	for(int i = 1; i<=m; i++) f[0][i] = i; //表示 a 有0个字符,b有i个需要增加i次
	for(int i = 1; i<=n; i++) f[i][0] = i;

	for(int i = 1; i<=n; i++){
		for(int j = 1; j<=m; j++){
//			f[i][j] = min({
//    			f[i-1][j] + 1,   // 删除 a[i]
//    			f[i][j-1] + 1,   // 在 a[i] 后插入 b[j]
//   				 f[i-1][j-1] + (a[i] != b[j])  // 替换:不同就+1,相同就+0
//				});
			f[i][j] = min(f[i-1][j] + 1, f[i][j-1] + 1);
			//可以先把一定大的求出来 
            if(a[i] == b[j]) {
                f[i][j] = min(f[i][j], f[i-1][j-1]);
            } else {
                f[i][j] = min(f[i][j], f[i-1][j-1] + 1);
            }
		}
	} 
	
	cout << f[n][m];
}

AcWing 899. 编辑距离

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
char str[N][N];
int f[N][N];
int n, m;

int edit_distance(char a[N], char b[N]){
	int la = strlen(a+1), lb = strlen(b+1);
	
	for(int i=1; i<=lb; i++) f[0][i] = i;
	for(int i=1; i<=la; i++) f[i][0] = i;
	
	for(int i = 1; i<=la; i++){
		for(int j = 1; j<=lb; j++){
			f[i][j] = min({f[i-1][j] + 1,  f[i][j-1] + 1, f[i-1][j-1]+(a[i] != b[j])});
		}
	}
	return f[la][lb];
}

int main(){
	cin >> n >> m;
	for(int i = 1; i<=n; i++) cin >> str[i] + 1;
	
	while(m--){
		char s[N];
		int limit;
		cin >> s+1 >> limit;
		int res =0 ;
		
		for(int i =1 ; i<=n; i++){
			if(edit_distance(str[i], s) <=limit) res++;
		}
		cout << res << endl;
	}
	return 0;
} 

区间DP

282. 石子合并

状态转移方程中的集合的表达 :flr = 把 l ~ r 这一段石子合并成一堆的最小代价。

从小大大列举区间 (len = 2; len<=n; len++)再枚举起点(i = 1; i+len-1<=n; i++)能枚举出所有区间为二的状态;for(int k = l; k<r; k++){ **flr = min(flr, flk + fk+1r+sr - sl-1);**并且能比较出区间怎么配分状态最小。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 310;
int f[N][N], s[N];
int n;

int main(){
	cin >> n;
	for(int i = 1; i<=n; i++) cin >> s[i];
	
	for(int i = 1; i<=n; i++) s[i] += s[i-1];
	
//	memset(f, 0x3f, sizeof f); 
	for(int len = 2; len<=n; len++){//这个区间要一直枚举到最大 
		for(int i = 1; i+len-1<=n; i++){//枚举起点 不能 i<n;整段都有起点 
			int l = i, r = i+len-1;
			//区间分为两半,计算每个区间求最小值 
			f[l][r] = 1e9;
			for(int k = l; k<r; k++){                  //区间最小 l-1
				f[l][r] = min(f[l][r], f[l][k] + f[k+1][r]+s[r] - s[l-1]);
			}
		}
	}
	cout << f[1][n];
	return 0;
}
相关推荐
budingxiaomoli3 分钟前
Spring日志
java·开发语言
牛油果子哥q6 分钟前
【C++ STL vector】C++ STL vector 终极精讲:动态数组底层原理、两倍扩容机制、迭代器失效、增删查改、性能剖析与工程避坑指南
开发语言·c++
贩卖黄昏的熊20 分钟前
flex 布局快速梳理
开发语言·javascript·css3·html5
天天进步201537 分钟前
Python全栈项目--校园智能宿舍管理系统
开发语言·python
CodeStats42 分钟前
从 CPU 指令到 JVM 进程:彻底讲透 Java 执行 main 方法时,类加载、主线程、栈帧入栈的完整底层逻辑
java·linux·开发语言
happymaker06261 小时前
LeetCodeHot100——42.接雨水
算法
阿正的梦工坊1 小时前
【Rust】09-泛型、Trait 与生命周期基础
开发语言·rust·c#
阿正的梦工坊1 小时前
【Rust】07-错误处理:Option、Result 与 ? 运算符
开发语言·算法·rust
烬羽2 小时前
从零理解树与二叉树:用 JS 带你手撕遍历和递归
javascript·数据结构