题目链接:http://www.wikioi.com/problem/1044/
题目其实就是最长上升子序列的变形。
下面是两个重要定理:
定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。 其对偶定理称为Dilworth定理:
定理2 令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。
这样问题的第一问是问一套系统最多能拦截多少个导弹,题目意思是每次不能超过上一次的高度,那么也就是求序列中最长不上升的子序列,代码中用的是O(n^2)的经典算法。
第二问呢,最少得要多少套系统才能拦截所有导弹,根据定理,最长上升子序列的长度对应的就是反链的最少个数,也就是最少的系统数。
代码实现采用的是O(nlogn)的算法,http://www.slyar.com/blog/longest-ordered-subsequence.html
1 #include <iostream> 2 #include <queue> 3 #include <climits> 4 #include <algorithm> 5 #include <memory.h> 6 #include <stdio.h> 7 #include <ostream> 8 #include <vector> 9 #include <list> 10 using namespace std; 11 12 vector<int> data; 13 int dp1[10000]; 14 int dp2[10000]; 15 int main() 16 { 17 int tmp; 18 while(scanf("%d",&tmp)!=EOF) 19 { 20 data.push_back(tmp); 21 } 22 int n = data.size(); 23 dp1[0] = 1; 24 int ans = 1; 25 int i; 26 for(i = 1; i < n ; ++i) 27 { 28 int _max; 29 _max = 0; 30 for(int j = 0 ; j < i ; ++j) 31 { 32 if(data[j] > data[i]) 33 { 34 _max = max(_max,dp1[j]); 35 } 36 } 37 dp1[i] = _max+1; 38 ans = max(ans,dp1[i]); 39 } 40 cout<<ans<<endl; 41 42 //O(nlogn)的算法求最长上升子序列 43 ans = 0; 44 dp2[0] = -1; 45 for(i = 0 ; i < n ; ++i) 46 { 47 if(data[i] > dp2[ans]) 48 { 49 dp2[++ans] = data[i]; 50 } 51 else 52 { 53 int low = 1; 54 int high = ans; 55 int mid; 56 while(low <= high) 57 { 58 mid = (low + high)/2; 59 if(data[i] > dp2[mid]) 60 { 61 low = mid+1; 62 } 63 else 64 { 65 high = mid-1; 66 } 67 } 68 dp2[low] = data[i]; 69 } 70 } 71 cout<<ans<<endl; 72 return 0; 73 }