1、DP写法
f [ i ] 表示以 i 结尾的子序列的最长长度。
g [ i ] 表示从尾部出发,以 i 结尾的子序列的最长长度。
那么假设选定 i 为最高点的话,合唱队形人数为f [ i ] + g [ i ] - 1 ,减一是因为 i 算了两次。
1 #include<iostream> 2 using namespace std; 3 const int N=110; 4 int a[N],f[N],g[N]; 5 int main(void){ 6 int n; 7 cin>>n; 8 for(int i=1;i<=n;i++){ 9 cin>>a[i]; 10 } 11 for(int i=1;i<=n;i++){//正着做一遍最长子序列 12 f[i]=1; 13 for(int j=1;j<i;j++){ 14 if(a[i]>a[j]){ 15 f[i]=max(f[i],f[j]+1); 16 } 17 } 18 } 19 20 for(int i=n;i>=1;i--){//反着做一遍最长子序列 21 g[i]=1; 22 for(int j=n;j>i;j--){ 23 if(a[i]>a[j]){ 24 g[i]=max(g[i],g[j]+1); 25 } 26 } 27 } 28 int res=0; 29 for(int i=1;i<=n;i++){//统计答案 30 res=max(res,f[i]+g[i]-1); 31 } 32 cout<<n-res; 33 return 0; 34 }
2、优化写法(贪心)
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int N=110; 5 int a[N],f[N],g[N],d[N]; 6 int main(void){ 7 int n; 8 cin>>n; 9 for(int i=1;i<=n;i++){ 10 cin>>a[i]; 11 } 12 13 //求f 14 memset(d,0,sizeof(d)); 15 d[0]=0; 16 int len=0; 17 for(int i=1;i<=n;i++){ 18 int l=0,r=len; 19 while(l<r){ 20 int mid=l+r+1>>1; 21 if(d[mid]<a[i]) l=mid; 22 else r=mid-1; 23 } 24 len=max(len,r+1); 25 f[i]=r+1; 26 d[r+1]=a[i]; 27 } 28 29 //求g 30 memset(d,0,sizeof(d)); 31 len=0; 32 for(int i=n;i>=1;i--){//必须从后往前,依据题意要求的g是从尾部到i的上升序列的最大长度 33 //而从前往后算递减的话(假设g[1]最大)g[1]=1,而他应该为n的。 34 int l=0,r=len; 35 while(l<r){ 36 int mid=l+r+1>>1; 37 if(d[mid]<a[i]) l=mid; 38 else r=mid-1; 39 } 40 len=max(len,r+1); 41 g[i]=r+1; 42 d[r+1]=a[i]; 43 } 44 45 46 //统计答案 47 int res=0; 48 for(int i=1;i<=n;i++){ 49 res=max(res,f[i]+g[i]-1); 50 } 51 cout<<n-res; 52 53 return 0; 54 }