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