数列问题

一、最大子段和

问题描述:给定n个整数(可能有负数)组成的序列a1,a2,…,an,求该序列的连续m个数的和的最大值,当所给的整数均为负数时和为0。

DP

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int a[maxn], dp[maxn];//dp[i]表示到第i个数的最大子段和 
int main() {
	int n; cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	dp[1] = a[1];
	int maxx = 0;//记录最大子段和 
	for(int i = 1; i <= n; i++) {
		dp[i] = max(a[i],/*从当前的数重新开始记录最大子段和*/ dp[i - 1] + a[i]/*加上a[i]后的最大子段和*/);
		maxx = max(maxx, dp[i]);
	}
	if(maxx < 0) cout << 0 << "\n";
	else cout << maxx << "\n";
	return 0;
} 

二、最长上升子串&最长上升子序列

1、子串:子串指的是字符串中连续的n个字符,如abcdefg中,ab,cde,fg等都属于它的字串。

2、子序列:子序列指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序。如abcdefg中,acdg,bdf属于它的子序列。

3、最长上升子串

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], dp[maxn];//dp[i]表示到第i项的最长子串长度 
int main() {
	int n; scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	int maxx = 0;
	for(int i = 1; i <= n; i++) {
		if(a[i] > a[i - 1]) dp[i] = dp[i - 1] + 1; 
		else dp[i] = 1;
		maxx = max(maxx, dp[i]);    
	} 
	printf("%d\n", maxx);
	return 0;
}  

4、最长上升子序列

(1)O(n^2) DP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], dp[maxn];//dp[i]表示前i个数以a[i]结尾的最长上升子序列长度
int main() {
	int n; scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		dp[i] = 1;//每个数都是一个子序列 
	}
	int maxx = 0;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j < i; j++) {
			if(a[i] > a[j]) dp[i] = max(dp[i], dp[j] + 1);
			maxx = max(maxx, dp[i]); 
		}
	}
	printf("%d\n", maxx);
	return 0;
} 
(2)O(nlogn)

lower_bound(begin, end, num):从数组的begin位置到end - 1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], ans[maxn];
int main() {
	int n; cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	ans[1] = a[1];
	int cnt = 1;
	for(int i = 1; i <= n; i++) {
		if(a[i] > ans[cnt]) ans[++cnt] = a[i];
		else {
			int pos = lower_bound(ans + 1, ans + cnt + 1, a[i]) - ans;
			ans[pos] = a[i];
		}
	}
	for(int i = 1; i <= cnt; i++) cout << ans[i] << " ";
	cout << "\n";
	cout << cnt << "\n";
	return 0;
} 

我们以数列6 4 3 2 1 8 5 6为例:

(1)ans[1] = a[1] = 6;

(2)进入循环a[2] (4) < ans[1] (6),pos = 1,ans[1] = 4;

(3)判断a[3] (3) < ans[1] (4),pos = 1,ans[1] = 3;

(4)判断a[4] (2) < ans[1] (3),pos = 1,ans[1] = 2;

(5)判断a[5] (1) < ans[1] (2),pos = 1,ans[1] = 1;

(6)判断a[6] (8) > ans[1] (1),ans[++1] = a[6] = 8;

此时ans数组:1,8

(7)判断a[7] (5) < ans[2] (8),pos = 2,ans[2] = a[7] = 5;

(8)判断a[8] (6) > ans[2] (5),ans[++2] = a[8] = 6;

ans数组:1,5,6

三、最长公共子串&最长公共子序列

1、最长公共子串:给定两个字符串,它们同时含有的最长子串则被称为最长公共子串。

2、最长公共子序列:给定两个字符串,它们同时含有的最长子序列则被称为最长公共子序列。

3、最长公共子串

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int a[maxn], b[maxn], dp[maxn][maxn];//dp[i][j]表示第一个字符串第i位和第二个字符串第j位的最长公共子串 
int main() {
	int l1, l2; cin >> l1 >> l2;
	for(int i = 1; i <= l1; i++) cin >> a[i];
	for(int i = 1; i <= l2; i++) cin >> b[i];
	int maxx = 0; 
	for(int i = 1; i <= l1; i++) {
		for(int j = 1; j <= l2; j++) {
			if(a[i] != b[j]) dp[i][j] = 0;
			else dp[i][j] = dp[i - 1][j - 1] + 1;
			maxx = max(maxx, dp[i][j]);
		}
	}
	cout << maxx << "\n";
	return 0;
}

4、最长公共子序列

#include <bits/stdc++.h>
using namespace std;
int a[10000], b[10000], dp[100][100];
int main() {
	int l1, l2;
	cin >> l1 >> l2;
	for(int i = 1; i <= l1; i++) cin >> a[i];
	for(int i = 1; i <= l2; i++) cin >> b[i]; 
	int ans = 0;
	for(int i = 1; i <= l1; i++){
		for(int j = 1; j <= l2; j++){
			if(i == 0 || j == 0) dp[i][j] = 0;
			else {
				dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
				if(a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1);
			}
			ans = max(ans, dp[i][j]);
		}
	}
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值