1. 程式人生 > >洛谷P1020 導彈攔截

洛谷P1020 導彈攔截

題目連結
思路1:

動態規劃,利用f[i]f[i]表示到第ii個導彈時所攔截的總導彈數,則有 f[i]=max(f[i],f[j]+1)i>j&&a[i]a[j]f[i]=max(f[i],f[j]+1) \quad i>j\&\&a[i]\le a[j] 對於第二問,“攔截所有導彈最少要配備多少套這種導彈攔截系統”,利用序列的不下降子序列最少劃分數等於上升序列的總長度這一原理,即是求最長上升子序列的長度。 f

2[i]=max(f2[i],f2[j]+1)i>j&&a[i]>a[j]f2[i]=max(f2[i],f2[j]+1) \quad i>j\&\&a[i]\gt a[j] 時間複雜度O(m2)O(m^2),過一半資料。

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

int f[100010
],f2[100010]; int a[100010],b[100010]; //f[i]=max(f[i],f[j]+1) i>j&&a[i]<=a[j] int main(int argc, char** argv) { int m=1,ans=-1,cnt=1; while(scanf("%d",&a[m])!=EOF){ f[m]=1,f2[m]=1;++m; continue; } --m; for(int i=1;i<=m;i++){ for(int j=1;j<i;j++){ if(a[i]<=a[j]) f[i]
=max(f[i],f[j]+1); } if(ans<f[i]) ans=f[i]; } for(int i=1;i<=m;i++){ for(int j=1;j<i;j++){ if(a[i]>a[j]) f2[i]=max(f2[i],f2[j]+1); } cnt=max(cnt,f2[i]); } printf("%d\n%d\n",ans,cnt); return 0; }
思路2:

利用f[i]f[i]維護最長非增序列的每個時刻的最大值,這時的f[i]f[i]已經不再是思路1中的用來表示到第ii個導彈時攔截的導彈總數,而是表示攔截到的第ii個導彈的最大值。舉個栗子,如這組資料90 103 99 83 102 70 86 70 99 71,由於我們求的是最長非增子序列,初始化f[1]=a[1]f[1]=a[1],當遇到a[2]&gt;f[1]a[2]\gt f[1]時,即更新f[1]f[1]的值為a[2]a[2];反之,如果a[2]f[1]a[2]\le f[1],則增加攔截的新導彈數f[2]=a[2]f[2]=a[2]。以此類推,最終更新得到的f[i]f[i]為103,102,99 ,71,70,表示可能攔截到的第ii個導彈的最大值為f[i]f[i]。 注意到f[i]f[i]始終為單調非增序列,故可以利用二分查詢提高效率。 時間複雜度O(mlog(m))O(mlog(m)),通過全部資料。

#include <iostream>
#include<cstdio>
using namespace std;
int f[100010],f2[100010];
int a[100010],b[100010];
//利用f[i]維護最長非增序列的每個時刻的最大值 
int main(int argc, char** argv) {
	int m=1;
	while(scanf("%d",&a[m])!=EOF){
		++m; 
		continue;
	}
	--m;
	
	f[1]=a[1];
	int ans=1,cnt=1;
	for(int i=2;i<=m;i++){
		if(a[i]<=f[ans])  f[++ans]=a[i];
		else{
			int l=1,r=ans;
			while(l<r){
				int mid=(l+r)>>1;
				if(f[mid]>=a[i]) l=mid+1;
				else r=mid;
			}
			f[l]=a[i];
		}
	}
	
	f2[1]=a[1];
	for(int i=2;i<=m;i++){
		if(a[i]>f2[cnt]) f2[++cnt]=a[i];
		else{
			int l=1,r=cnt;
			while(l<r){
				int mid=(l+r)>>1;
				if(f2[mid]<a[i]) l=mid+1;
				else r=mid;
			}
			f2[l]=a[i];
		}
	} 
	
	printf("%d\n%d\n",ans,cnt);
	return 0;
}