本篇着重进行动态规划例题展开,关于动态规划的基本模型和基础知识,请移步
DP动态规划专题(一)动态规划基本模型
【例1】最长不下降序列
㈠问题描述:
设有由n个不相同的整数组成的数列,记为:b(1)、b(2)、……、b(n)且b(i)<>b(j) (i<>j),若存在i1<i2<i3< … < ie 且有b(i1)<b(i2)< … <b(ie)则称为长度为e的不下降序列。程序要求,当原数列出之后,求出最长的不下降序列。
- 例如13,7,9,16,38,24,37,18,44,19,21,22,63,15。
- 例中13,16,18,19,21,22,63就是一个长度为7的不下降序列,
- 同时也有7 ,9,16,18,19,21,22,63长度为8的不下降序列。
㈡算法分析:
根据动态规划的原理,由后往前进行搜索(当然从前往后也一样)。
- 对b(n)来说,由于它是最后一个数,所以当从b(n)开始查找时,只存在长度为1的不下降序列;
- 若从b(n-1)开始查找,则存在下面的两种可能性:
①若b(n-1)<b(n)则存在长度为2的不下降序列b(n-1),b(n)。
②若b(n-1)>b(n)则存在长度为1的不下降序列b(n-1)或b(n)。 - 一般若从b(i)开始,此时最长不下降序列应该按下列方法求出:
在b(i+1),b(i+2),…,b(n)中,找出一个比b(i)大的且最长的不下降序列,作为它的后继。
㈢数据结构:
为算法上的需要,定义一个整数类型二维数组b(N,3)
- b(I,1)表示第I个数的数值本身;
- b(I,2)表示从I位置到达N的最长不下降序列长度
- b(I,3)表示从I位置开始最长不下降序列的下一个位置,若b[I,3]=0则表示后面没有连接项。
㈣求解过程:
①从倒数第二项开始计算,后面仅有1项,比较一次,因63>15,不符合要求,长度仍为1。
②从倒数第三项开始其后有2项,需做两次比较,得到目前最长的不下降序列为2,如下表:
㈤一般处理过程是:
①在i+1,i+2,…,n项中,找出比b[I,1]大的最长长度L以及位置K;
②若L>0,则b[I,2]:=L+1;b[I,3]:=k;
最后本题经过计算,其数据存储表如下:
code
#include<iostream>
using namespace std;
int main() {
int n,i,j,l,k,b[200][10];
cout<<"input n:"<<endl;
cin>>n;
for (i=1; i<=n; i++) {
//输入序列的初始值
cin>>b[i][1];//value
b[i][2]=1;//long
b[i][3]=0;//position
}
for (i=n-1; i>=1; i--) {
//求最长不下降序列
l=0;
k=0;
for (j=i+1; j<=n; j++)
if ((b[j][1]>b[i][1])&&(b[j][2]>l)) {
l=b[j][2];
k=j;
}
if (l>0) {
b[i][2]=l+1;
b[i][3]=k;
}
}
k=1;
for (j=1; j<=n; j++) //求最长不下降序列的起始位置
if (b[j][2]>b[k][2]) k=j;
cout<<"max="<<b[k][2]<<endl; //输出结果
while (k!=0) {
//输出最长不下降序列
cout<<' '<<b[k][1];
k=b[k][3];
}
}
【例2】拦截导弹
【题目】
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,导弹数不超过1000),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
【样例】
INPUT
389 207 155 300 299 170 158 65
OUTPUT
6(最多能拦截的导弹数)
2(拦截所有导弹最少要配的系统数)
【算法分析】
第一问即经典的最长不下降子序列问题,可以用一般的DP算法,也可以用高效算法,但这个题的数据规模不需要。
用a[x]表示原序列中第x个元素,b[x]表示长度为x的不下降子序列的长度。当处理第a[x]时,可查找它可以连接到长度最大为多少的不下降子序列后(即与部分b[x]比较)。假设可以连到长度最大为maxx的不下降子序列后,则b[x]:=maxx+1。b数组被赋值的最大值就是第一问的答案。
第二问用贪心法即可。每颗导弹来袭时,使用能拦截这颗导弹的防御系统中上一次拦截导弹高度最低的那一套来拦截。若不存在符合这一条件的系统,则使用一套新系统。
【参考程序】(顺推法)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int i,j,k,x,n,maxx,m,a[10000],b[10000],h[10000]