合唱隊形——LIS坑爹的二分優化

題目

【題目描述】
N位同學站成一排,音樂老師要請其中的(N-K)位同學出列,使得剩下的K位同學排成合唱隊形。
合唱隊形是指這樣的一種隊形:設K位同學從左到右依次編號爲1,2…,K,他們的身高分別爲T1,T2,…,TK,則他們的身高滿足T1<…Ti+1>…>TK(1<=i<=K)。
你的任務是,已知所有N位同學的身高,計算最少需要幾位同學出列,可以使得剩下的同學排成合唱隊形。
【輸入格式】
輸入文件chorus.in的第一行是一個整數N,表示同學的總數。第一行有n個整數,用空格分隔,第i個整數Ti是第i位同學的身高(釐米)。
【輸出格式】
輸出文件chorus.out包括一行,這一行只包含一個整數,就是最少需要幾位同學出列。
【樣例輸入】
8
186 186 150 200 160 130 197 220
【樣例輸出】
4
【數據規模】
對於50%的數據,保證有n<=200;
對於100%的數據,保證有n<=20000,1<=Ti<=1000000。

解析

該題本身平淡無奇,只是一個標準的LIS動態規劃。但是!該題擴大了數據範圍!
使得本來O(n2)的時間複雜度過不了,於是只有優化(關鍵是我考試時沒怎麼注意)。
這裏便會有二分查找的優化方法。

思路

枚舉中間的人i,以他爲終點正向和倒向求兩次LIS,用l[i]和r[i]分別表示以i結尾LIS長度。然後ans = n - l[i] + r[i] + 1;
注意到N <= 20000,所以必須使用logN的算法求LIS
使用d[]臨時存儲LIS,依次將序列中的數a[i]按以下條件加入d[]中:如果a[i] > d[len](d[len]是d[]中的最後一個數),則d[++len] = a[i],否則在d[]中找第一個比a[i]大或等(必須取到=,否則相同的數將不被替換,而是替換成了第一個比它大的數 )的數,將其替換,這樣做的目的是使d[]更有可能成爲一個更長的序列。我們注意到d[]是一個遞增的序列,則在完成查找時可使用二分的方法。
注意在這個過程中存儲l[i]和r[i]

代碼

#include<iostream>
#include<cstring>
using namespace std;

int a[20001],l[20001],r[20001],rem[20001];
int n;

int main()
{
	freopen("chorus.in","r",stdin);
	freopen("chorus.out","w",stdout);
	
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i];
	
	int len=0;	
	for(int i=1;i<=n;i++)
	{
		if(a[i]>rem[len])
		{
			rem[++len]=a[i];
			l[i]=len;
		}
		else
		{
			int left=1,right=len;
			while(left<right)
			{
				int mid=(left+right)/2;
				if(rem[mid]>=a[i])right=mid;
				//此處必須取到=,否則相同的數將不被替換,而是替換成了第一個比它大的數 
				else left=mid+1;
			}
			rem[left]=a[i];
			l[i]=left;
		}	
	}
	
	memset(rem,0,sizeof(rem));

	len=0;
	for(int i=n;i>=1;i--)
	{
	
		if(a[i]>rem[len])
		{
			rem[++len]=a[i];
			r[i]=len;
		}
		else
		{
			int left=1,right=len;
			while(left<right)
			{
				int mid=(left+right)/2;
				if(rem[mid]>=a[i])right=mid;
				//此處必須取到=,否則相同的數將不被替換,而是替換成了第一個比它大的數 
				else left=mid+1;
			}
			rem[left]=a[i];
			r[i]=left;
		}	
	}
	
	int maxx=0;
	for(int i=1;i<=n;i++)
		maxx=max(maxx,l[i]+r[i]-1);
	
	cout<<n-maxx<<endl;
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章