合唱队形
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了合唱队形相关的知识,希望对你有一定的参考价值。
原题链接:https://www.luogu.org/problem/show?pid=1091#sub
应该是一道很经典的动规题了。它分属于线性DP范围。
题意要求我们要让最少的人出列,达到一个先上升再下降的效果,类似于一个波峰。
实际上,只需要找出这个序列上最长上升子序列和最长下降子序列就好。
不过这次我用的方法和我之前dp知识点整理的那里不太一样。
我开了两个数组,一个叫up,一个叫down,分别代表以当前元素为止的最长上升子序列和最长下降子序列的长度,把这个数组预处理出来。
然后去枚举每一个up[i]和down[i],取一个最大值作为ans,也就是最长的序列长度,既然序列长度最长,那么出列的人自然就最少,正确性显然。
下面说几个细节。
1.读入的时候要把所有的up和down的初始值设为1,因为如果找不到最长上升(下降)子序列,元素本身可以认为是一个序列。
2.节点I在up数组和down数组中都算了一遍,所以输出的时候应该要输出n-(ans-1)。
附参考代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define maxn 123 5 using namespace std; 6 int n,a[maxn],up[maxn],down[maxn]; 7 int main(){ 8 cin >> n; 9 for (int i=1;i<=n;i++){ 10 cin >> a[i]; 11 up[i] = 1; 12 down[i] = 1; 13 } 14 for (int i=2;i<=n;i++){ 15 int l = 0; 16 for (int j=1;j<=i-1;j++) 17 if (a[j]<a[i] && up[j]>l) 18 l = up[j]; 19 up[i] = l+1; 20 } 21 for (int i=n-1;i>=1;i--){ 22 int l = 0; 23 for (int j=i+1;j<=n;j++) 24 if (a[i] > a[j] && down[j]>l) 25 l = down[j]; 26 down[i] = l+1; 27 } 28 int ans = 0; 29 for (int i=1;i<=n;i++) 30 ans = max(up[i]+down[i],ans); 31 cout << n - ans + 1 << endl; 32 return 0; 33 34 }
以上是关于合唱队形的主要内容,如果未能解决你的问题,请参考以下文章