【题目描述】 Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
【输入描述】 Input Description
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)
【输出描述】 Output Description
输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【样例输入】Sample Input
389 207 155 300 299 170 158 65
【样例输出】 Sample Output
6
2
【数据范围及提示】 Data Size & Hint
导弹的高度<=30000,导弹个数<=20
【以下部分转自大神】
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得证。
【】
由此,“导弹拦截”就转化为求“最长上升子序列”的长度;
此处先给出求“最长严格上升子序列”的算法;
【题目描述】 Description
给一个数组a1, a2 ... an,找到最长的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk。
输出长度即可。
【输入描述】 Input Description
第一行,一个整数N。
第二行 ,N个整数(N < = 5000)
【输出描述】 Output Description
输出K的极大值,即最长不下降子序列的长度
【样例输入】 Sample Input
5
9 3 6 2 7
【样例输出】 Sample Output
3
//
#include<cstdio> #include<cstring> using namespace std; const int MAXN=100000; int a[MAXN],ans[MAXN],sum,temp; int main() { int n,j; scanf("%d",&n); for(int i=1;i<=n;i++) ans[i]=1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { temp=0; for(j=1;j<i;j++) if(a[j]<a[i]&&ans[j]>temp)temp=ans[j]; ans[i]+=temp; } for(int i=1;i<=n;i++) if(ans[i]>sum)sum=ans[i]; printf("%d\n",sum); return 0; } 核心代码
for(int i=1;i<=n;i++) { temp=0; for(j=1;j<i;j++) if(a[j]<a[i]&&ans[j]>temp)temp=ans[j]; ans[i]+=temp; }把其中的< >取等即可 以下给出“拦截导弹”的算法
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int a[30][3]={0,0}; int b[30]={0}; int c[30]={0}; int main() { int n=0,high; while(scanf("%d",&high)==1) { //shu ru n++; a[n][1]=high; } int max=0; a[n][2]=1; for(int i=n;i>=1;i--) for(int j=i;j<=n;j++) { if(a[i][1]>a[j][1]) { if(a[j][2]>=max) { max=a[j][2]; a[i][2]=a[j][2]+1; } } else if(j==n&&a[i][2]==0) a[i][2]=1; } max++; b[1]=a[1][1]; int sl=1; for(int i=1;i<=n;i++) { for(int j=1;j<=sl;j++) { if(a[i][1]<=b[j]) { b[j]=a[i][1]; break; } else if(a[i][1]>b[j]&&j==sl) { sl++; b[j+1]=a[i][1]; } } } printf("%d\n%d\n",max,sl); return 0; }