输入的第一行是一个整数N(2 <= N <= 100),表示同学的总数。 第一行有n个整数,用空格分隔,第i个整数Ti(130 <= Ti <= 230)是第i位同学的身高(厘米)。
可能包括多组测试数据,对于每组数据, 输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
8 186 186 150 200 160 130 197 220
4
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 101;
int arr[MAXN];
int dp1[MAXN];
int dp2[MAXN];
bool Compare(int a,int b)
{
return a>b;
}
int main()
{
int n;
while(scanf("%d",&n) != EOF)
{
for(int i=0; i<n; ++i)
{
scanf("%d",&arr[i]);
dp1[i] = 1;
dp2[i] = 1;
}
for(int i=0; i<n; ++i)
{
for(int j=0; j<i; ++j)
{
if(arr[j] < arr[i])
{
//dp[i] = dp[j]+1;
dp1[i] = max(dp1[i],dp1[j]+1);
}
}
}
for(int i=n-1; i>=0; --i)
{
for(int j=n-1; j>i; --j)
{
if(arr[j] < arr[i])
{
dp2[i] = max(dp2[i],dp2[j]+1);
}
}
}
int maxAnswer = 0;
for(int i=0; i<n; ++i)
{
int answer = dp1[i] + dp2[i];
maxAnswer = max(maxAnswer,answer);
}
printf("%d\n",n-maxAnswer+1);
}
return 0;
} 正向一次最长递增子序列,反向再做一次,再遍历找到以左右两边最多的节点#include <stdio.h>
int main()
{
int k,i,j,max;
while(scanf("%d",&k)!=EOF)
{
int a[k],b[k],c[k];
for(i=0;i<k;i++)
{
scanf("%d",&a[i]);
b[i]=1;
c[i]=1;
}
for(i=1;i<k;i++)
{
for(j=0;j<i;j++)
{
if(a[i]>a[j])
b[i]=b[i]>b[j]+1?b[i]:b[j]+1;
}
}
for(i=k-2;i>=0;i--)
{
for(j=k-1;j>i;j--)
{
if(a[i]>a[j])
c[i]=c[i]>c[j]+1?c[i]:c[j]+1;
}
}
for(i=0,max=1;i<k;i++)
{
if(b[i]+c[i]>max)
max=b[i]+c[i];
}
printf("%d\n",k-max+1);
}
return 0;
} //前后两次运用LIS(最长递增子序列)
//由于合唱队形是先递增后递减
//a数组是输入序列 b数组是a数组的逆序列
//先对a数组求最长递增子序列
//再对b数组求最长递增子序列
//由于合唱队形最后一人到中间最高的人是一个递增序列
//对应的b数组序列就是从第一位开始求递增序列,这里求递增还是递减要想清楚
//想要出列的人最少,那么留下的人就要最多
//每一个队员对应的LISa值的意思是,假设他自己为最高,则他前面有多少人(包括他自己)
//每一个队员对应的LISb值的意思是,假设他自己为最高,则他后面有多少人(包括他自己)
//所以要找留下的人最多的方法是,依次比较每一个队员对应的LISa和LISb的值的和,最大值即为所求
//求出列人数的方法:总人数减去 (max(LISa+LISb)-1)
//因为LISa和LISb加起来时,最高的那个队员被加了两次
#include<stdio.h>
int main(){
int n;
int a[101],b[101],LISa[101],LISb[101]={0};
int i,j;
while(scanf("%d",&n)!=EOF){
for(i=0;i<n;i++){
scanf("%d",&a[i]);
b[n-i-1]=a[i]; //b数组是a数组的逆序列
}
//a数组求最长递增子序列
LISa[0]=1;
for(i=1;i<n;i++){
int max=0;
for(j=i-1;j>=0;j--){
if(a[j]<a[i]){
if(LISa[j]>max)
max=LISa[j];
}
}
LISa[i]=max+1;
}
//b数组求最长递增子序列
LISb[0]=1;
for(i=1;i<n;i++){
int max=0;
for(j=i-1;j>=0;j--){
if(b[j]<b[i]){
if(LISb[j]>max)
max=LISb[j];
}
}
LISb[i]=max+1;
}
int result=0;
for(i=0;i<n;i++){
if(LISa[i]+LISb[n-i-1]>result){
result=LISa[i]+LISb[n-i-1];
}
}
printf("%d\n",n-result+1); //这里是化简后,把括号去了,所以最后是+1
}
} /*
动态规划:求最长先增后减序列的值
用dp[i]来表示以a[i]为结尾的最长递增子序列的值
用dp2[i]来表示以a[i]为开头的最长递减子序列的值
那么最长先增后减序列的值就是
max(ans,dp[i]+dp2[i]-1); 这里要-1,因为在dp和dp2中,a[i]这个数本身被计算了两次
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int arr[maxn];
int dp[maxn]; // 从左至右最长递增子序列
int dp2[maxn]; // 从右至左最长递减子序列
int main() {
int n;
while(~scanf("%d",&n)) {
for(int i=0; i<n; i++) { // input
scanf("%d",&arr[i]);
dp[i] = 1;
dp2[i] = 1;
}
for(int i=0; i<n; i++) { // 从左到右,求最长递增子序列
for(int j=0; j<i; j++) {
if(arr[j]<arr[i]) {
dp[i] = max(dp[i],dp[j]+1);
}
}
}
for(int i=n-1; i>=0; i--) {
for(int j=n-1; j>i; j--) { // 从右到左,求最长递减子序列
if(arr[j]<arr[i]) {
dp2[i] = max(dp2[j]+1,dp2[i]);
}
}
}
int ans = INT_MAX; // 保存结果
for(int i=0; i<n; i++) {
ans = min(ans,n-dp[i]-dp2[i]+1); // 重复减了自身两次
}
printf("%d\n",ans);
}
} #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
while (cin >> n) {
// 从左往右升序DP,dp_asc[i]表示i位置为结束的最长递子序列长度
int dp_asc[n];
// 从左往右降序DP, dp_desc[i]表示以i为结尾的最长递减子序列长度
int dp_desc[n];
int std[n];
for (int i = 0; i < n; ++i) {
scanf("%d", &std[i]);
}
// 升序DP dp[i] = max{1, dp[j] + 1 | j < i && std[i] > std[j]}
for (int i = 0; i < n; ++i) {
dp_asc[i] = 1;
for (int j = 0; j < i; ++j) {
if (std[i] > std[j]) {
dp_asc[i] = max(dp_asc[i], dp_asc[j] + 1);
}
}
}
// 降序DP dp[i] = max{1, dp[j] + 1 | j < i && std[i] < std[j]}
// 这里从后往前推,注意if判断
for (int i = n - 1; i >=0 ; --i) {
dp_desc[i] = 1;
for (int j = n - 1; j > i ; --j) {
if (std[i] > std[j]) {
dp_desc[i] = max(dp_desc[i], dp_desc[j] + 1);
}
}
}
int maxN = -1;
// 遍历两个DP
for (int i = 0; i < n; ++i) {
maxN = max(maxN, dp_asc[i] + dp_desc[i] - 1); // 减去重复计算的i
}
cout << n - maxN << endl;
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,t[110],maxlen[110],rmaxlen[110];
cin>>n;
fill(maxlen,maxlen+n,1);
fill(rmaxlen,rmaxlen+n,0);
for(int i=0;i<n;i++) cin>>t[i];
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(t[j]<t[i]){
maxlen[i]=max(maxlen[i],maxlen[j]+1);
}
}
}
reverse(t,t+n);
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(t[j]<t[i]){
rmaxlen[i]=max(rmaxlen[i],rmaxlen[j]+1);
}
}
}
for(int i=0;i<n;i++) maxlen[i]+=rmaxlen[n-i-1];
cout<<(n-*max_element(maxlen,maxlen+n));
return 0;
} 两个动态规划,找出左到右递增时最长子序列长度,右到左递增时最长子序列长度。
然后相同下标相加长度相加-1就得到题目要求的队形长度。就可以得到去除的人数
#找出一个方向上的递增子序列长度
def findAscSubString(peopleNum, heightList):
ascSubNum = [1] * peopleNum
for i in range(peopleNum):
if heightList[i] != min(heightList[:i + 1]):
for j in range(i):
if heightList[i] > heightList[j]:
if ascSubNum[i] < ascSubNum[j] + 1:
ascSubNum[i] = ascSubNum[j] + 1
return ascSubNum
#得到去除的队员人数
def findRemovePeople(peopleNum, heightList):
leftPeople = findAscSubString(peopleNum, heightList) #左到右递增
rightPeople = findAscSubString(peopleNum, heightList[::-1]) #右到左递增
rightPeople.reverse() #反转得到下标对应
stayPeople = 0
for i in range(peopleNum):
if stayPeople < leftPeople[i] + rightPeople[i]:
stayPeople = leftPeople[i] + rightPeople[i]
return peopleNum - stayPeople + 1
while True:
try:
peopleNum = int(input())
heightList = list(map(int, input().split()))
print(findRemovePeople(peopleNum, heightList))
except Exception:
break #include<stdio.h>
int main(){
int i,j,n,max,ans;
int high[101];
int F1[101],F2[101];
while(scanf("%d",&n)!=EOF){
for(i=1;i<=n;i++)
scanf("%d",&high[i]);
for(i=1;i<=n;i++){
max=1;
for(j=1;j<i;j++)
if(high[i]>high[j]&&F1[j]+1>max)
max=F1[j]+1;
F1[i]=max;
}
for(i=n;i>=1;i--){
max=1;
for(j=n;j>i;j--)
if(high[i]>high[j]&&F2[j]+1>max)
max=F2[j]+1;
F2[i]=max;
}
ans=101;
for(i=1;i<=n;i++){
if(ans>n-(F1[i]+F2[i]-1))
ans=n-(F1[i]+F2[i]-1);
}
printf("%d\n",ans);
}
return 0;
}
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
int n = in.nextInt();
int[] arr = new int[n];
int[] left = new int[n];
int[] right = new int[n];
for(int i=0;i<n;i++){
arr[i] = in.nextInt();
}
//动态规划
//求最长递增子序列
for(int i=0;i<n;i++){
left[i] = 1;
for(int j=i-1;j>=0;j--)
if(arr[j]<arr[i])
left[i] = Math.max(left[i], left[j]+1);
}
//求从右边数起的最长递减子序列
for(int i=n-1;i>=0;i--){
right[i] = 1;
for(int j=i+1;j<n;j++)
if(arr[i]>arr[j])
right[i] = Math.max(right[i], right[j]+1);
}
int max=0;
for(int i=0;i<n;i++){
max = Math.max(max, left[i]+right[i]-1);
}
System.out.println(n-max);
}
}
}
#include <bits/stdc++.h>
using namespace std;
int num[100];
int dp1[100];
int dp2[100];
int main(){
int count;
while(cin>>count){
for(int i=0;i<count;i++){
cin>>num[i];
}
for(int i=0;i<count;i++){
dp1[i]=1;
for(int j=0;j<i;j++){
if(num[i]>num[j]){
dp1[i]=max(dp1[j]+1,dp1[i]);
}
}
}
for(int i=count-1;i>=0;i--){
dp2[i]=1;
for(int j=count-1;j>i;j--){
if(num[i]>num[j]){
dp2[i]=max(dp2[i],dp2[j]+1);
}
}
}
int total=0;
for(int i=0;i<count;i++){
total=max(dp1[i]+dp2[i]-1,total);
}
cout<<count-total<<endl;
}
}
枚举每个位置,从左边找最长上升子序列,从右边开始找最长下降子序列,两个数相加就是题目所说的唱歌的人数。
/*动态规划的最长子列问题,分别从前往后和从后往前寻找以i点为尾的最长子列,寻找两个子列和的最大值*/
#include<stdio.h>
int main()
{
int N,i,j,max;
while(scanf("%d",&N)!=EOF)
{
int high[200],marka[200],markb[200];
for(i=1;i<=N;i++)
{
scanf("%d",&high[i]);
marka[i]=markb[i]=1; /*每点为尾的子列长度最小都为1*/
}
for(i=2;i<=N;i++) /*从前往后寻找以i点为尾的最长递增子列*/
{
max=marka[i];
for(j=i-1;j>=1;j--)
{
if(high[i]>high[j])
max=(max>marka[j]+1)?max:marka[j]+1;
}
marka[i]=max;
}
for(i=N-1;i>=1;i--) /*从后往前寻找以i点为尾的最长递增子列*/
{
max=markb[i];
for(j=i+1;j<=N;j++)
{
if(high[i]>high[j])
max=(max>markb[j]+1)?max:markb[j]+1;
}
markb[i]=max;
}
max=marka[1]+markb[1]; /*寻找点i两个子列和的最大值*/
for(i=2;i<=N;i++)
max=(max>marka[i]+markb[i])?max:marka[i]+markb[i];
printf("%d\n",N-max+1);
}
return 0;
}
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
while (in.hasNextInt()) {
int cnt = in.nextInt();
int[] height = new int[cnt];
for (int i = 0; i < cnt; i++) {
height[i] = in.nextInt();
}
System.out.println(processChorusFormation(height, cnt));
}
in.close();
}
private static int processChorusFormation(int[] height, int len) {
// TODO Auto-generated method stub
int res = 0;
int[] left = new int[len];
int[] right = new int[len];
left[0] = right[len - 1] = 1;
for (int i = 1; i < len; i++) {
left[i]=1;
for (int k =0; k<i; k++) {
if (height[i] > height[k]) {
left[i] = Math.max(left[i], left[k] + 1);
}
}
}
for (int i = len - 2; i >= 0; i--) {
right[i]=1;
for (int k = len - 1; k > i; k--) {
if (height[i] > height[k]) {
right[i] = Math.max(right[i], right[ k] + 1);
}
}
}
for (int i = 0; i < len; i++) {
if (left[i] + right[i] - 1 > res) {
res = left[i] + right[i] - 1;
}
}
return len-res;
}
}
#include<iostream>
using namespace std;
int dp1[101];
int dp2[101];
int T[101];
int main()
{
int N;
while(cin >> N)
{
for(int i = 0;i < N;i++)
{
cin >> T[i];
}
int maxnum = 0;
for(int i = 0;i < N;i++)
{
dp1[i] = 1;
for(int j = 0;j < i;j++)
{
if(T[i] > T[j])
{
dp1[i] = max(dp1[i],dp1[j] + 1);
}
}
dp2[N - i - 1] = 1;
for(int j = N - 1;j > N - i - 1;j--)
{
if(T[N - i - 1] > T[j])
{
dp2[N - i - 1] = max(dp2[N - i - 1],dp2[j] + 1);
}
}
}
for(int i = 0;i < N;i++)
{
maxnum = max(dp1[i] + dp2[i],maxnum);
}
cout << N - maxnum + 1<< endl;
}
return 0;
} //两次dp求正反序的最长递增子序列长度,这样代码比较简洁高效
#include<iostream>
(720)#include<algorithm>
using namespace std;
const int maxn=105;
int a1[maxn];
int a2[maxn];
int dp1[maxn];//正序以a1[i]为最长递增子序列长度
int dp2[maxn];//逆序以a2[i]为最长递增子序列长度
int main()
{
int n;
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>a1[i];
a2[n-1-i]=a1[i];//逆序
}
for(int i=0;i<n;i++)
{
dp1[i]=1;//初始化为1
dp2[i]=1;
for(int j=0;j<i;j++)
{
if(a1[i]>a1[j])
{
dp1[i]=max(dp1[i],dp1[j]+1);//正序求以a1[i]递增子序列长度
}
if(a2[i]>a2[j])
{
dp2[i]=max(dp2[i],dp2[j]+1);//逆序求以a2[i]递增子序列长度
}
}
}
int ans=0;
for(int i=0;i<n;i++)
{
dp1[i]+=dp2[n-1-i];//相同元素作为末尾的递增子序列长度相加,一个从左到右,一个从右到左
ans=max(ans,dp1[i]);//选出最大的符合要求的长度
}
cout<<n-ans+1<<endl;//两个序列相加时末尾元素计算了两次需要减去1,最终需要出列的人数即n-(ans-1)
}
return 0;
} def hechang(s, n): m = 0 dp1 = [1]*n dp2 = [1]*n # dp1[0] = 1 # dp2[0] = 1 for i in range(1, n): for j in range(0, i): if s[i] > s[j]: dp1[i] = max(dp1[i], dp1[j]+1) for i in range(n-2, -1, -1): for j in range(n-1, i, -1): if s[i] > s[j]: dp2[i] = max(dp2[i], dp2[j]+1) for i in range(n): a = dp2[i] + dp1[i] m = max(a, m) return n - m + 1 n = int(input()) s = list(map(int, input().split())) print(hechang(s, n))
#include <iostream>
#include <vector>
using namespace std;
int main() {
int num;
while (cin >> num) {
vector<int> stu(num);
for (int i = 0; i < num; i++)
cin >> stu[i];
vector<int> add(num, 1); // 前i个元素的最长递增子序列的长度
for (int i = 1; i < num; i++)
for (int j = i; j >= 0; j--)
if (stu[j] < stu[i])
add[i] = max(add[i], add[j] + 1);
vector<int> reduce(num, 1); // 后i个元素的最长递减子序列的长度
for (int i = num - 1; i >= 0; i--)
for (int j = i; j < num; j++)
if (stu[j] < stu[i])
reduce[i] = max(reduce[j] + 1, reduce[i]);
int k = 0; // 剩下K位同学
for (int i = 0; i < num; i++)
k = max(k, (add[i] + reduce[i]) - 1);
cout << num - k << endl;
}
return 0;
} #include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
int n;
while(cin>>n){
vector<int> t(n);
for(int i=0;i<n;i++){
cin>>t[i];
}
//求以t[i]结尾最长递增子序列长度dp1[i]
vector<int> dp1(n);
dp1[0] = 1;
for(int i=1;i<n;i++){
dp1[i] = 1;
for(int j=0;j<i;j++){
if(t[j]<t[i]) dp1[i] = max(dp1[i],dp1[j]+1);
}
}
//求以t[i]开头的最长递减子序列长度
vector<int> dp2(n);
for(int i=n-1;i>=0;i--){
dp2[i] = 1;
for(int j=i;j<n;j++){
if(t[j]<t[i]) dp2[i] = max(dp2[i],1+dp2[j]);
}
}
//求以上两序列和减一
vector<int> k(n);
for(int i=0;i<n;i++){
k[i] = dp1[i]+dp2[i]-1;
}
cout<<n-*max_element(k.begin(),k.end())<<endl;
}
return 0;
}