线性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来记录路径,f[i] = f[j] + 1; g[i] = 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数组的结尾比较大的就进入,当a[i] <= f[cnt] 的时,我们就用a[i]去替代数组 p 中的第一个**大于等于a[i]**的数

我们用a[i]去替代f[i]的含义是:以a[i]为最后一个数的严格单调递增序列,这个序列中数的个数为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 ] 是由当前位置的字符是否相等来判断的, 如果相等就可以直接转移到 f[i-1][j-1] +1。不相等的话,可以对**f[i-1][j] (不包含a[i]的最大值),f[i][j-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. 最短编辑距离

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

f[ i ][ j ] =min({

// f [ i - 1][ j ] + 1, // 删除 a[i](删之前i-1个 = j个

// f [ i ][ j-1 ] + 1, // 在 a[i] 后插入 b[j](添加之前前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. 石子合并

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

从小大大列举区间 (len = 2; len<=n; len++)再枚举起点(i = 1; i+len-1<=n; i++)能枚举出所有区间为二的状态;for(int k = l; k<r; k++){ **f[l][r] = min(f[l][r], f[l][k] + f[k+1][r]+s[r] - s[l-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;
}
相关推荐
IronMurphy20 小时前
【算法四十三】279. 完全平方数
算法
墨染天姬20 小时前
【AI】Hermes的GEPA算法
人工智能·算法
papership21 小时前
【入门级-数据结构-3、特殊树:完全二叉树的数组表示法】
数据结构·算法·链表
smj2302_7968265221 小时前
解决leetcode第3911题.移除子数组元素后第k小偶数
数据结构·python·算法·leetcode
山甫aa21 小时前
差分数组 ----- 从零开始的数据结构
数据结构
早日退休!!!21 小时前
《数据结构选型指南》笔记
数据结构·数据库·oracle
九转成圣21 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
Beginner x_u21 小时前
链表专题:JS 实现原理与高频算法题总结
javascript·算法·链表
SmartRadio21 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
丑八怪大丑1 天前
Java数据结构与集合源码
数据结构