題目描述
N位同學站成一排,音樂老師要請其中的(N-K)位同學出列,使得剩下的K位同學排成合唱隊形。
合唱隊形是指這樣的一種隊形:設K位同學從左到右依次編號爲1,2,…,K,他們的身高分別爲T_1,T_2,…,T_K, 則他們的身高滿足<…<>>…>。
你的任務是,已知所有N位同學的身高,計算最少需要幾位同學出列,可以使得剩下的同學排成合唱隊形。
輸入格式
共二行。
第一行是一個整數,表示同學的總數。
第二行有n個整數,用空格分隔,第i個整數是第i位同學的身高(釐米)。
輸出格式
一個整數,最少需要幾位同學出列。
輸入輸出樣例
輸入 #1複製
8
186 186 150 200 160 130 197 220
輸出 #1複製
4
說明/提示
對於50%的數據,保證有;
對於全部的數據,保證有。
思路:
其實思路就是挺好想的,因爲數據規模不大,所以枚舉以第 i 個數爲最高點時的隊列長度,然後選出最長的即可得到排除最少的人數。
而如何計算最長隊列呢?很顯然,只要算出 i 之前的最長升序子序列(並且末尾元素小於第 i 個元素)和 i 之後的最長降序子序列(並且首元素小於第 i 個元素)。
代碼:
/*思路:轉換思路 -> 求最長升序子序列和最長降序子序列 */
#include <cstdio>
#include <climits>
#include <algorithm>
using namespace std;
const int MAX = 110;
int N;
struct data
{
int d,l,r;
}datas[MAX];
int results[MAX];
void get_maxDecrease()
{
for(int i = N;i >= 1;i--){
datas[i].r = 1;
for(int j = N;j > i;j--)
if(datas[i].d > datas[j].d)
datas[i].r = max(datas[i].r,datas[j].r + 1);
}
}
//獲取以site爲結尾的最大長度
void get_maxIncrease()
{
for(int i = 1;i <= N;i++){
datas[i].l = 1;
for(int j = 1;j < i;j++)
if(datas[i].d > datas[j].d)
datas[i].l = max(datas[i].l,datas[j].l + 1);
}
}
void init()
{
get_maxIncrease();
get_maxDecrease();
return ;
}
int get_right(int i)
{
int myMax = 0;
for(int j = N;j > i;j--)
if(datas[j].d < datas[i].d&&myMax < datas[j].r)
myMax = datas[j].r;
return myMax;
}
int get_left(int i)
{
int myMax = 0;
for(int j = 1;j < i;j++)
if(datas[j].d < datas[i].d&&myMax < datas[j].l)
myMax = datas[j].l;
return myMax;
}
int get_ans()
{
int num = 0;
for(int i = 1;i <= N;i++){
int left = get_left(i);
int right = get_right(i);
left = i-1-left;
right = N-i-right;
num = left + right;
results[i] = num;
}
int myMin = INT_MAX;
for(int i = 1;i <= N;i++)
if(myMin > results[i])
myMin = results[i];
return myMin;
}
int main()
{
//讀入數據並初始化:
scanf("%d",&N);
for(int i = 1;i <= N;i++)
scanf("%d",&datas[i].d);
init();
//處理:
int ans = get_ans();
//輸出:
printf("%d",ans);
return 0;
}
總結:
這道題我一開始做錯了,至於原因我也不陳述了(比較麻煩),但結論是,對於熟悉或者工具類的算法我們不要隨意根據題更改,而要儘量像調用API一樣使用它們。這樣做有兩個好處,一是不容易出錯,二是久而久之這些常用代碼(例如本題的最長子序列、二分查找之類的)會越來越熟悉。