输入的第一行是一个整数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; }
#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; }
/* 扩展:排队从低到高、从高到低 */ /* 思路:分别找从左到右最长递增子序列和从右到左最长递增子序列,然后进行操作判断 */ #include<iostream> #include<cstring> using namespace std; const int MAXN = 101; int A[MAXN]; int dpL[MAXN]; int dpR[MAXN]; void Init(){ memset(A,0,sizeof(0)); fill(dpL,dpL+MAXN,1); fill(dpR,dpR+MAXN,1); } // 从左、右两个方向更新两个dp int UpLevel(int n){ // 1.更新左 dpL for(int i=0;i<n;i++){ for(int j=0;j<i;j++){ if(A[j] < A[i]){ dpL[i] = max(dpL[i],dpL[j]+1); } } } // 2.更新右 dpR for(int i=n-1;i>=0;i--){ for(int j=n-1;j>i;j--){ if(A[j] < A[i]){ dpR[i] = max(dpR[i],dpR[j]+1); } } } // 3.统计 int i=0; // 始终指向 dpL int j=0; // 始终指向 dpR int result = 1000000; while(i<n){ int num = n - (dpL[i]+dpR[j]-1); result = min(num,result); i++; j++; } return result; } int main(){ int n; while(cin>>n){ Init(); for(int i=0;i<n;i++){ cin >> A[i]; } int answer = UpLevel(n); cout << answer << endl; } }
#include <algorithm> #include <iostream> #include <vector> using namespace std; // Longest Increasing Subsequence vector<int> LIS(const vector<int>& nums) { vector<int> res(nums.size(), 1); for (int i = 0; i < nums.size(); i++) for (int j = i - 1; j>= 0; j--) { if (nums[i] > nums[j]) res[i] = max(res[i], res[j] + 1); } return res; } vector<int> RELIS(const vector<int>& nums) { vector<int> res(nums.size(), 1); for (int i = nums.size() - 1; i >= 0; i--) for (int j = i + 1; j < nums.size(); j++) { if (nums[i] > nums[j]) res[i] = max(res[i], res[j] + 1); } return res; } int main() { int N; while (cin >> N) { vector<int> nums(N); for (int i = 0; i < N; i++) { cin >> nums[i]; } vector<int> res = LIS(nums); vector<int> tmp = RELIS(nums); for (int i = 0; i < N; i++) { res[i] += tmp[i]; } cout << N - *max_element(res.begin(), res.end()) + 1 << endl; } }