微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

合唱队形 (最长上升子序列 + 最长下降子序列)

已经有:

pow(5,2)+sqrt(9)

天没更新了。
(还是那句话,运行上面程序的时候别忘了cmath头文件呀)

在这里插入图片描述

接下来话不多说,直接上正题:

【题目】

[NOIP2004 提高组] 合唱队形

题目描述

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

合唱队形是指这样的一种队形:设 k k k 位同学从左到右依次编号为 1 , 2 , 1,2, 1,2, , k ,k ,k,他们的身高分别为 t 1 , t 2 , t_1,t_2, t1,t2, , t k ,t_k ,tk,则他们的身高满足 t 1 < ⋯ < t i > t i + 1 > t_1< \cdots <t_i>t_{i+1}> t1<<ti>ti+1> > t k ( 1 ≤ i ≤ k ) >t_k(1\le i\le k) >tk(1ik)

你的任务是,已知所有 n n n 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

第一行是一个整数 n n n 2 ≤ n ≤ 100 2\le n\le100 2n100),表示同学的总数。

第二行有 n n n 个整数,用空格分隔,第 i i i 个整数 t i t_i ti 130 ≤ t i ≤ 230 130\le t_i\le230 130ti230)是第 i i i 位同学的身高(厘米)。

输出格式

一个整数,最少需要几位同学出列。

样例 #1

样例输入 #1

8
186 186 150 200 160 130 197 220

样例输出 #1

4

提示

对于 50 % 50\% 50% 的数据,保证有 n ≤ 20 n \le 20 n20

对于全部的数据,保证有 n ≤ 100 n \le 100 n100

原题链接原题链接

看大佬们有用单调队列做的,有用二分+贪心做到O(nlogn)的,我作为一个蒟蒻只能用O(n2)的简单线性dp方法做。

【算法分析】

很显然,这是一道求最优解的题目,所以肯定用dp做。
再深入分析一下,左边其实是上升序列,右边就是下降序列,似乎就是求所有上升子序列长度+下降子序列长度的最大值,那就只需要两个dp数组分别储存就可以了。
但是有个问题,有可能会重叠,这该怎么办呢?
记录下降子序列时,我们可以倒序记录,即从后往前记录,这样子就不会存在重叠的问题。
注意审题,问题问的是几人出列,而不是队列中留几人!

【数据储存】

一个变量n来储存人数,然后每个人的身高用数组存储即可。

【dp数组含义】

两个dp数组含义其实就是:
一个是储存最长上升子序列,后一个是储存最长下降子序列。
即:

int dp1[];//最长上升子序列
int dp2[];//最长下降子序列
/*
dp1[i] => 从前往后数至第 i 个数的最长上升子序列长度
dp2[i] => 从后往前数 i 个数后的最长下降子序列
*/

【填表】

由dp数组含义可得,第一个数组正序遍历,第二个数组倒序遍历即可。即:

for(int i=1;i<=n;i++){
	递推公式(dp1数组)
}
for(int i=n;i>=1;i--){
	递推公式(dp2数组)
}

【递推公式】

因为是由最长上升子序列+最长下降子序列组成的,所以递推公式和这两者就可以了。

完整填表代码如下:

for(int i=1;i<=n;i++){//最长上升子序列
		for(int j=1;j<i;j++){
			if(a[i]>a[j]){
				dp1[i]=max(dp1[i],dp1[j]+1);
			}
		}
	}
	for(int i=n;i>=1;i--){//倒序求最长下降子序列
		for(int j=n;j>i;j--){
			if(a[i]>a[j]){//因为是倒序遍历,所以是a[i]>a[j],而不是a[i]<a[j]
				dp2[i]=max(dp2[i],dp2[j]+1);
			}
		}
	}

【初始化】

因为每个数本身就是一个元素,所以全部初始化成 1 即可。
注意:不能用memset!!!因为它是按字节初始化!!!

完整AC代码如下:

注释版:

#include <iostream>//cin,cout
#include <cstdio>//scanf,printf
#include <iomanip>//setprecision,setw
#include <algorithm>//max,min,sort…函数文件
#include <string>//字符串,getline(),size()…… 
#include <cstring>//字符数组,cin.getline(),strlen()…… 
#include <cmath>//数学函数库,abs,sqrt,
#include <climits>//INT_MAX,INT_MIN
#include <ctime>//clock()
#include <cstdlib>//exit()
#include <queue>//STL
#include <stack>//STL
#include <deque>//STL
#include <vector>//STL
#include <map>//STL
#include <list>//STL
using namespace std;
const int maxn = 105 ;
int n;//储存学生人数 
int a[maxn];//储存各个学生的身高 
int dp1[maxn];//储存最长上升子序列 
int dp2[maxn];//储存倒序的最长下降子序列 
int ans;//储存最大的dp1[i]+dp2[i] 
int main(){
	//输入 
	cin>>n;//输入人数 
	for(int i=1;i<=n;i++){
		cin>>a[i];//输入学生身高  
	}
	//初始化
	for(int i=1;i<=n;i++){
		dp1[i]=1;//初始化dp1数组 
		dp2[i]=1;//初始化dp2数组 
	}
	//填表 
	for(int i=1;i<=n;i++){//填dp1数组,即最长上升子序列数组 
		for(int j=1;j<i;j++){
			if(a[i]>a[j]){
				dp1[i]=max(dp1[i],dp1[j]+1);
			}
		}
	}
	for(int i=n;i>=1;i--){//填dp2数组,即倒序的最长下降子序列数组 
		for(int j=n;j>i;j--){
			if(a[i]>a[j]){
				dp2[i]=max(dp2[i],dp2[j]+1);
			}
		}
	}
	//求最大值 
	for(int i=1;i<=n;i++){
		ans=max(ans,dp1[i]+dp2[i]);//求最大值 
	}
	//输出结果 
	cout<<n-ans+1;//仔细审题,问的是要有几人出列 
	return 0;
}

纯净版:

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <algorithm>
#include <string>
#include <cstring> 
#include <cmath>
#include <climits>
#include <ctime>
#include <cstdlib>
#include <queue>
#include <stack>
#include <deque>
#include <vector>
#include <map>
#include <list>
using namespace std;
const int maxn = 105 ;
int n,ans;
int a[maxn],dp1[maxn],dp2[maxn];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dp1[i]=1;
		dp2[i]=1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[i]>a[j]){
				dp1[i]=max(dp1[i],dp1[j]+1);
			}
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=n;j>i;j--){
			if(a[i]>a[j]){
				dp2[i]=max(dp2[i],dp2[j]+1);
			}
		}
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,dp1[i]+dp2[i]);
	}
	cout<<n-ans+1;
	return 0;
}

原文地址:https://www.jb51.cc/wenti/3284468.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐