天天看點

P1020 飛彈攔截

  • 前面

參考部落格:Dilworth定理 Dilworth定理是個啥東東

P1020 飛彈攔截

的代碼:P1020 飛彈攔截 能過50%

對于STL中 二分查找的函數,自定義cmp:題解 P1020 【飛彈攔截】

對于LIS的

P1020 飛彈攔截

寫法:O(NlogN)做法:貪心+二分

沒想到,本來以為是個簡單的DP,咋整來這麼多知識??

  • 題目描述 

P1020 飛彈攔截

題目就是

①先求最長的,非上升的,子序列的長度

②求這樣的子序列 有幾個,根據那什麼定理,就是求出來最長的,上升的子序列長度。

這個什麼定理:偏序集能劃分成的最少的全序集個數等于最大反鍊的元素個數。

偏序關系是 >=,全序集的個數(最長的非上升子序列的個數)== 最大反鍊(最長的上升子序列)的長度 

  • 兩種方法

最長的非上升子序列長度,最長的上升子序列長度,是同樣的思路。

P1020 飛彈攔截

的思想是動态規劃。

P1020 飛彈攔截

的思想是 貪心+二分。

  • 動态規劃

動态規劃的思想,簡單說來。

// 50 %
//P1020

const int maxn=1e5+5;
int num[maxn];
int dp[maxn];

int main()
{
	int ii=1;
	while(scanf("%d",&num[ii])!=EOF)
		ii++;
	ii--;
	int len=ii;
	
	for(int i=1;i<=len;i++)
		dp[i]=1;
		
	for(int i=2;i<=len;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(num[j]>=num[i])
				dp[i]=max(dp[i],dp[j]+1);
		}

	}
	
	int ans1=0;
	for(int i=1;i<=len;i++)
	{
	//	cout<<dp[i]<<" ";
		ans1=max(ans1,dp[i]);
	}
	cout<<ans1<<endl;
	
	for(int i=1;i<=len;i++)
		dp[i]=1;
	for(int i=2;i<=len;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(num[j]<num[i])
				dp[i]=max(dp[i],dp[j]+1);
		}
	}
	int ans2=0;
	for(int i=1;i<=len;i++)
	{
	//	cout<<dp[i]<<" ";
		ans2=max(ans2,dp[i]);
	}
	cout<<ans2<<endl;
	
}
           

以非上升序列為例:

P1020 飛彈攔截

 數組表示 以第 

P1020 飛彈攔截

 個元素作為結尾,最長的非上升序列的長度。

是以,初始化全部為1。

遞推關系式為:

P1020 飛彈攔截

說明 包含第 

P1020 飛彈攔截

 個元素的長度,是由前面的,某個長度推來的。(滿足條件emmm),大概這個意思。

最後要注意的是,需要循環周遊

P1020 飛彈攔截

 數組,找到最大值。這是因為

P1020 飛彈攔截

的定義,但是最長的長度并不一定包含了最後一個元素,并不一定是

P1020 飛彈攔截

.

  • 貪心+二分

P1020 飛彈攔截

表示表示長度為 

P1020 飛彈攔截

 的LIS結尾元素的最小值。 

利用貪心的思想,對于一個上升子序列,顯然目前最後一個元素越小,越有利于添加新的元素,這樣LIS長度自然更長。 

就是這個部落格,講的肥腸好

P1020 飛彈攔截

 如果你不想寫二分的代碼,你可以使用自帶的STL函數。但是由于upper_bound函數,要求是遞增序列,我們在求最長的非上升子序列長度時,dp數組的必然是非遞增序列,是以需要重新定義函數,

這裡的greater<int>()就是c++友情提供的友善的大于函數,這樣就不用自己動手寫一個cmp函數了(霧).

const int maxn=1e5+5;
int num[maxn];
int dp[maxn];

int main()
{
	int ii=1;
	while(scanf("%d",&num[ii])!=EOF)
		ii++;
	ii--;
	int len=ii;
	
	
	int pos=1;
	dp[1]=num[1];
	for(int i=2;i<=len;i++)
	{
		if(num[i]<=dp[pos])
		{
			dp[++pos]=num[i];
		}
		else
			dp[upper_bound(dp+1,dp+pos+2,num[i],greater<int>() )-dp]=num[i];
	}
	cout<<pos<<endl;
	
	//最長上升子序列 
	for(int i=1;i<=len;i++)
		dp[i]=9999999;
	pos=0;
	dp[1]=num[1];
	for(int i=2;i<=len;i++)
	{
		if(num[i]>dp[pos])
		{
			dp[++pos]=num[i];
		}
		else
			dp[lower_bound(dp+1,dp+pos+2,num[i])-dp]=num[i];
	}
	cout<<pos<<endl;
	
}
           

繼續閱讀