解题记录 合唱队形 双向求最长上升序列

第一个问题,如何求最长上升序列

题意: n 位同学站成一排,音乐老师要请其中的 n−kn-kn−k 位同学出列,使得剩下的 kkk 位同学排成合唱队形。

合唱队形是指这样的一种队形:设 k 位同学从左到右依次编号为 1,2 … ,k,他们的身高分别为 t1,t2…,tk​,则他们的身高满足中间最高两边递减

你的任务是,已知所有 n 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形
如果我们用常规dp做法的话,时间复杂度为n^2.

for(int i = 1;i <= n; i++)//从1到n求最长升 
    for(int j = 0; j < i; j++) if(a[i] > a[j])
        dp[i] = max(dp[i], dp[j] + 1);

如果用标准求最长上升序列,LIS时间复杂度为n*logn

for(int i = 2; i <= n; i++){
		if(a[i] > dp[cnt]) dp[++cnt] = a[i];
		else{
			int p = lower_bound(dp + 1, dp + cnt + 1, a[i]) - dp;
			dp[p] = a[i];
		} 
	}
	cout << cnt << endl;

区别:
第一种虽然时间复杂度高,但是他能存储数列中前 n 位数字的最长上升序列

第二种最后只能求 n 位

也就对应了一句俗话,时间复杂度越高的算法,其功能也就越多(不绝对正确)

https://www.luogu.com.cn/problem/P1091

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
int a[1000005] = {0};
int dp[2][100005] = {0};

int main() {
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++){
		scanf("%d", a + i);
	}
	
	for(int i = 1; i <= n; i++)
	for(int j = 0; j < i; j++)
	if(a[i] > a[j]) dp[0][i] = max(dp[0][i], dp[0][j] + 1);
	
	for(int i = n; i >= 1; i--)
	for(int j = n + 1; j > i; j--)
	if(a[i] > a[j]) dp[1][i] = max(dp[1][i], dp[1][j] + 1);
	
	int ans = 0;
	for(int i = 1; i <= n; i++){
		ans = max(ans, dp[0][i] + dp[1][i] - 1);
	}
	cout << n - ans << endl;
	return 0;
}

第二个二重循环是求最长下降序列

直接参照第一个倒过来就行了,注意i,j的初始化问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值