经典的导弹拦截问题
題目:http://wikioi.com/problem/1044/
?
?
題意:一種導彈攔截系統的第一發炮彈能夠到達任意的高度,但是以后每一發炮彈都不能高于前一發的高度。某天,雷達捕捉
到敵國的導彈來襲。由于該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。輸入導彈依次飛來的高
度,計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統?
?
?
分析:
第一問很簡單,就是求最長不上升子序列。對于第二問不能用貪心的方法來做,因為有反例:7 5 4 1 6 3 2.
我們把第二問的問題抽象出來,那就是:把一個數列劃分成最少的最長不升子序列,這里我們要介紹一個很優美的定理。
?
Dilworth定理:對于一個偏序集,最少鏈劃分等于最長反鏈長度。
Dilworth定理的對偶定理:對于一個偏序集,其最少反鏈劃分數等于其最長鏈的長度。
?
也就是說把一個數列劃分成最少的最長不升子序列的數目就等于這個數列的最長上升子序列的長度。
?
下面來說說這個定理是怎么來的:
?
偏序集的定義:偏序是在集合X上的二元關系≤(這只是個抽象符號,不是“小于或等于”,它滿足自反性、反對稱性和傳遞
性)。即,對于X中的任意元素a,b和c,有:
?
(1)自反性:a≤a;
(2)反對稱性:如果a≤b且b≤a,則有a=b;
(3)傳遞性:如果a≤b且b≤c,則a≤c 。
?
帶有偏序關系的集合稱為偏序集。
?
?
令(X,≤)是一個偏序集,對于集合中的兩個元素a、b,如果有a≤b或者b≤a,則稱a和b是可比的,否則a和b不可比。
在這個例子(反鏈)中元素Ri<=Rj是指(i<=j) and (ai>=aj)
?
一個反鏈A是X的一個子集,它的任意兩個元素都不能進行比較。
一個鏈C是X的一個子集,它的任意兩個元素都可比。
?
【定理】
在X中,對于元素a,如果任意元素b,都有a≤b,則稱a為極小元。
定理1:令(X,≤)是一個有限偏序集,并令r是其最大鏈的大小。則X可以被劃分成r個但不能再少的反鏈。
?
其對偶定理稱為Dilworth定理:
令(X,≤)是一個有限偏序集,并令m是反鏈的最大的大小。則X可以被劃分成m個但不能再少的鏈。
雖然這兩個定理內容相似,但第一個定理證明要簡單一些。此處就只證明定理1。
?
證明:設p為最少反鏈個數
(1)先證明X不能劃分成小于r個反鏈。由于r是最大鏈C的大小,C中任兩個元素都可比,因此C中任兩個元素都不能屬于同一反
鏈。所以p>=r。
(2)設X1=X,A1是X1中的極小元的集合。從X1中刪除A1得到X2。注意到對于X2中任意元素a2,必存在X1中的元素a1,使得
a1<=a2。令A2是X2中極小元的集合,從X2中刪除A2得到X3……,最終會有一個Xk非空而Xk+1為空。于是A1,A2,…,Ak就是X的
反鏈的劃分,同時存在鏈a1<=a2<=…<=ak,其中ai在Ai內。由于r是最長鏈大小,因此r>=k。由于X被劃分成了k個反鏈,因此
r>=k>=p。
(3)因此r=p,定理1得證。
?
【解決】
要求最少的覆蓋,按照Dilworth定理
最少鏈劃分 = 最長反鏈長度
所以最少系統 = 最長導彈高度上升序列長度。
?
?
?
Dilworth定理的應用
?
題目:http://poj.org/problem?id=1065
?
題意:給定n個二元組,設第一個元素為a[],第二個元素為b[],求最少的劃分數使得每一種劃分中a[]和b[]不是下降序列。
?
分析:這個問題是二元組的最少鏈劃分,那么我們以a[]為關鍵字大小進行排序,如果a[]中相同就按照b[]排序,根據
Dilworth定理,然后題目就變成了求b[]序列中最長嚴格下降子序列長度了。
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h>using namespace std; const int N = 5005; const int INF = ~0U>>1;struct Node {int x,y; };Node a[N],t[N]; int d[N];bool cmp(Node a,Node b) {return a.x < b.x || (a.x == b.x && a.y < b.y); }int Binary_Search(int l,int r,int x) {while(l < r){int m = (l + r) >> 1;if(x <= d[m]) r = m;else l = m + 1;}return l; }int Work(Node a[],int n) {d[0] = -1;int max = -1;int len = 1;for(int i=1;i<=n;i++){d[len] = INF;int j = Binary_Search(0,len,a[i].y);if(j == len) len++;d[j] = a[i].y;}return len - 1; }int main() {int n,T;scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);sort(a+1,a+n+1,cmp);for(int i=1;i<=n;i++)t[n-i+1] = a[i];printf("%d\n",Work(t,n));}return 0; }?
題目:http://poj.org/problem?id=3636
?
題意:跟上題基本一樣,只是把上題的條件由l <= l' and w <= w'改為了l < l' and w < w'。
?
分析:二者區別在于前者是大于等于關系,而后者必須是大于的。這就導致了二者排序的方式不一樣,第一個參數都是從小到
大排,但是第二個參數就有所區別了。前者要求是包含等于的,所以第2個參數也是從小到大排;而后者則不包含等于所以要從
大到小排 。因為w升序,h降序可以保證w相等時,一定不會出現覆蓋的情形。另外上題是求最長的嚴格下降子序列,而本題要
求最長的不上升子序列。所以,排序這樣:
bool cmp(Node a,Node b) {return a.x < b.x || (a.x == b.x && a.y > b.y); }?
?
題目:http://poj.org/problem?id=1548
?
題意:在一個矩形區域內,機器人從左上角出發,每次只能沿著現在位置的下方和右方移動,在移動過程中收拾垃圾,一直到區域
的右下角,現在給出所有垃圾的位置,求解最少需要多少個機器人才能將這些垃圾清理成功?
?
分析:可以利用Dilworth定理,先排序,然后求最長下降子序列。跟POJ1065基本一樣。
#include <iostream> #include <string.h> #include <algorithm> #include <stdio.h>using namespace std; const int N = 5005; const int INF = ~0U>>1;struct Node {int x,y; };Node a[N],t[N]; int d[N];bool cmp(Node a,Node b) {return a.x < b.x || (a.x == b.x && a.y < b.y); }int Binary_Search(int l,int r,int x) {while(l < r){int m = (l + r) >> 1;if(x <= d[m]) r = m;else l = m + 1;}return l; }int Work(Node a[],int n) {d[0] = -1;int max = -1;int len = 1;for(int i=1;i<=n;i++){d[len] = INF;int j = Binary_Search(0,len,a[i].y);if(j == len) len++;d[j] = a[i].y;}return len - 1; }int main() {while(true){int n = 0;int x,y;while(scanf("%d%d",&x,&y)){if(x == -1 && y == -1) return 0;if(x == 0 && y == 0) break;n++;a[n].x = x;a[n].y = y;}sort(a+1,a+n+1,cmp);for(int i=1;i<=n;i++)t[n-i+1] = a[i];printf("%d\n",Work(t,n));}return 0; }
?
題目:http://poj.org/problem?id=1631
?
題意:給定一個二分圖,二分圖左右兩邊的頂點數相同,有些頂點之間連有邊,選出最多的邊使得這個二分圖中所有的邊都不相交。
分析:基本上跟POJ1065和POJ3636的題目一樣,轉化一下就行了。
?
?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
- 上一篇: 大河家
- 下一篇: 数塔问题和最长上升子序列问题