最少攔截系統Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 12985 Accepted Submission(s): 5171
Problem Description
某國爲了防禦敵國的導彈襲擊,發展出一種導彈攔截系統.但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能超過前一發的高度.某天,雷達捕捉到敵國的導彈來襲.由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈.
怎麼辦呢?多搞幾套系統唄!你說說倒蠻容易,成本呢?成本是個大問題啊.所以俺就到這裏來求救了,請幫助計算一下最少需要多少套攔截系統.
Input
輸入若干組數據.每組數據包括:導彈總個數(正整數),導彈依此飛來的高度(雷達給出的高度數據是不大於30000的正整數,用空格分隔)
Output
對應每組數據輸出攔截所有導彈最少要配備多少套這種導彈攔截系統.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
2
Source
Recommend
JGShining
|
====================================算法分析======================================
一、貪心算法:
顯然第一想法就是貪心麼,但是貪亦有道,我知道和珅之所以名揚千古的原因了。。。
在contest時我想到的貪心思路是:讓每一套系統攔截儘可能多的導彈,也就是在尚未決定攔截系統的導彈序列中選取攔截其
最長不上升子序列中的導彈。
然後,然後就是WA的血與淚。
後來看題解的時候找到了這樣一個反例,導彈序列爲:6 5 1 7 3 2。
根據以上的貪心思路,第一套系統就應該攔截導彈 6 5 3 2 ,那剩下的導彈 1 7 就不得不另用兩套系統來攔截,這樣總共就需
要三套系統。
而事實上,最佳方案應該是,讓一套系統攔截 6 5 1 ,一套系統攔截 7 3 2 ,總共只需兩套系統。
從上面的例子可以看出,由於攔截系統之間的相互影響,讓每一套系統攔截儘可能多的導彈的貪心思路是錯誤的。
那麼正確的貪心思路呢?應該是使得攔截每一顆導彈的代價最小!
根據題意顯然應該將新增一套系統作爲最大代價,也就說只要已經使用的這些系統能夠攔截當前導彈那就不應該再新增一套系統
來攔截。
而在已經使用的能夠攔截當前導彈的系統中應該選擇哪套系統來攔截呢?根據貪心思路顯然應該讓能夠攔截的導彈高度最低的系
統來攔截。
根據以上分析可以得到這樣一份算法:
用一個數組record來記錄已經使用的系統能夠攔截的導彈高度。
如果當前導彈高度大於record中的所有值,則新增一套系統來攔截,並將當前導彈高度(也就是之後新增系統能夠攔截的導彈高
度)加入record數組。
否則在record數組中選取大於當前導彈高度的所有值中最小的那個,讓其對應的系統來攔截,並更新其能夠攔截的導彈高度爲當
前導彈高度。
這裏注意到一個關鍵:只要record是不降序的,那麼無論是加入元素還是修改元素,都不會破壞record不降序的性質!
因而record一直是不降序的!那麼之前所說的查找操作就可以通過O(logN)的二分來實現!故而算法的時間複雜度爲O(N*logN)。
二、最長不下降子序列:
有沒有發現上述的貪心算法其實就是求導彈序列的最長不下降子序列的O(N*logN)的算法?
但是我想了N久還是沒想出從題目直接切入到最長不下降子序列的思維過程。。。
總算是找到直接切入的思維過程了,其實就是倆定理:
Dilworth定理:對於一個偏序集,鏈的最少劃分數等於其最長反鏈的長度。
Dilworth定理的對偶定理:對於一個偏序集,其反鏈的最少劃分數等於其最長鏈的長度。
Dilworth定理貌似比較難證,暫時就不管它了。
Dilworth定理的對偶定理的證明參見:http://blog.sina.com.cn/s/blog_9634532001019znf.html,有時間得弄懂。
如果學過離散數學,理解一下定理還是不難的(書到用時方恨少哇~)。
對於這個題目而言,偏序關係就是小於等於,偏序集就是導彈序列,鏈就是不上升子序列,反鏈就是上升子序列。
所求的導彈序列的不上升子序列的最少劃分數就等於其最長上升子序列的長度!
=======================================代碼=======================================
一、貪心算法。
#include<stdio.h>
int N,Height[1005];
int Greedy()
{
int sum=0,record[1005];
record[sum++]=Height[0]; //使用一套系統攔截第一顆導彈
for(int i=1;i<N;++i)
{
if(Height[i]>record[sum-1]) //新增一套系統攔截當前導彈
{
record[sum++]=Height[i]; continue;
}
int l=0,r=sum-1;
while(l<r) //貪心原理選取系統攔截當前導彈
{
int m=(l+r)>>1;
if(record[m]<Height[i]) { l=m+1; }
else { r=m; }
}
record[l]=Height[i];
}
return sum;
}
int main()
{
while(scanf("%d",&N)==1)
{
for(int i=0;i<N;++i)
{
scanf("%d",&Height[i]);
}
printf("%d\n",Greedy());
}
return 0;
}
二、最長不下降子序列(用了自己寫的模板)。
/*#######################################[ LOS Templet Of Lyz ]#######################################*/
typedef bool BOOL;
typedef unsigned int UINT;
template < typename SEQUTYPE >
//CMP:返回所求序列中前後元素的比較關係("<","<=",">",">="即對應"升序","不降序","降序","不升序")
UINT LOS(SEQUTYPE *Sequ , UINT nSize , BOOL (*CMP)(SEQUTYPE*,SEQUTYPE*) )
{
UINT Len , *MinID = new UINT [ nSize + 1 ];
MinID[ Len = 1 ] = 0;
for( UINT i = 1 ; i < nSize ; ++i )
{
if( ! CMP( Sequ + MinID[1] , Sequ + i ) ) { MinID[1] = i; continue; }
if( CMP( Sequ + MinID[Len] , Sequ + i ) ) { MinID[ ++Len ] = i; continue; }
int L = 1 , R = Len;
while( L < R )
{
int M = ( L + R ) >> 1;
if( CMP( Sequ + MinID[ M + 1 ] , Sequ + i ) ) { L = M + 1; }
else { R = M; }
}
MinID[ L + 1 ] = i;
}
delete [] MinID;
return Len;
}
/*#######################################[ LOS Templet Of Lyz ]#######################################*/
#include<stdio.h>
int N,Height[1005];
bool Cmp(int *IntNum1,int *IntNum2)
{
return *IntNum1<=*IntNum2;
}
int main()
{
while(scanf("%d",&N)==1)
{
for(int i=0;i<N;++i)
{
scanf("%d",&Height[i]);
}
printf("%d\n",LOS(Height,N,Cmp));
}
return 0;
}