序列型动态规划总结

原题来自维基OI

最长上升子序列

题目描述:

序列 { 1,2,...,n } 的一个子序列是指序列 { i1, i2, ……, ik },其中 1<=i1 < i2 < …… < ik<=n, 序列 { a1, a2, ……, an } 的一个子序列是指序列 { ai1, ai2, ……, aik },其中 { i1, i2, ……, ik } 是 { 1, 2, ……, n } 的一个子序列.同时,称 k 为此子序列的长度.

如果 { ai1, ai2, ……, aik } 满足 ai1 ≤ ai2 ≤ …… ≤ aik,则称之为上升子序列.如果不等号都是严格成立的,则称之为严格上升子序列.同理,如果前面不等关系全部取相反方向,则称之为下降子序列和严格下降子序列. 

长度最长的上升子序列称为最长上升子序列.本问题对于给定的整数序列,请求出其最长严格上升子序列的长度.

输入描述:第一行,一个整数N;第二行,N个整数(N <= 5000)

输出描述:输出最长严格上升子序列(以下简称子序列)的长度

题目分析:典型的动态规划题,用动态规划的方式分析吧,设读入的整数存在数组origin里面,长度为len,也就是输入中所说的整数N,用f数组来保存子序列的长度,f[i]表示以origin[i]为结尾的数组长度,考虑怎样求f[i],以f[i]为结尾的最长严格上升子序列,那么origin[i]一定比子序列中前一个大,换句话说,只要在origin[i]前面(origin[1]~origin[i-1])比它大的数再加上origin[i],就可以成为一个严格上升子序列,其长度为f[1~i-1]+1.重新捋一遍,只要origin[i]比origin[1]~origin[i-1]中的一个大,那么以它为结尾的严格上升子序列长度为f[1~i-1]+1,而最大长度就是其最大值.状态转移方程f[i] = max(f[j]) + 1;(1 <= j < i && origin[j] < origin[i])

AC代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int origin[501], f[501], len;
 8     cin >> len;
 9     f[1] = 1;
10     for (int i = 1; i <= len; ++i) cin >> origin[i];
11     for (int i = 2; i <= len; ++i)
12     {
13         int m = 1;
14         for (int j = 1; j < i; ++j)
15         {
16             if (origin[j] < origin[i])
17                 m = max(m, f[j] + 1);
18         }
19         f[i] = m;
20     }
21     cout << *max_element(&f[1], &f[len+1]);
22 }

 

线段覆盖

题目描述:数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~1000000,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大.n<=1000

输入描述:第一行一个整数n,表示有多少条线段.接下来n行每行三个整数, ai bi ci,分别代表第i条线段的左端点ai,右端点bi(保证左端点<右端点)和价值ci.

题目分析:设len储存线段条数,3个数组,left储存线段左端点坐标,right储存线段右端点坐标,f起初储存线段的价值(储存线段价值相当于初始化f),计算过程中f[i]表示以第i条线段为结尾的不覆盖线段集的最大价值.有两条不覆盖的线段,那么把它们放到一个集合里,价值为它们的价值和.考虑计算f[i],如果第i条线段不覆盖与1~i-1条线段中任意一条,那么f[i]就可以加入到以该线段为结尾的线段集中,则该线段集的价值为原线段集的价值+f[i],(自认为说的有些啰嗦),那么要求f[i]只要求f[1] ~ f[i-1]符合条件(符合条件就是不覆盖,即right[j] <= left[i])的最大值再加上f[i]即可,状态转移方程为f[i] = max(f[1] ~ f[i-1]) + f[i](right[j] <= left[i]).等等,你怎么知道线段就是从左到右放在数轴上的?所以要在开头对右端点排序.

AC代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 int Left[1001] = {1000001}, Right[1001] = {1000001}, f[1001] = {1000001}, len;
 5 void superswap(int i, int j)//每次交换是交换整条线段,所以要交换三个
 6 {
 7     swap(Left[i], Left[j]);
 8     swap(Right[i], Right[j]);
 9     swap(f[i], f[j]);
10 }
11 void selection_sort()//选择排序,按照右端点排序
12 {
13     for (int i = 1; i <= len; ++i)
14     {
15         int maxsub = 0;
16         for (int j = i; j <= len; ++j)
17         {
18             if (Right[maxsub] > Right[j])
19                 maxsub = j;
20         }
21         superswap(i, maxsub);
22     }
23 }
24 int main()
25 {
26     cin >> len;
27     for (int i = 1; i <= len; ++i)
28         cin >> Left[i] >> Right[i] >> f[i];
29     selection_sort();
30     for (int i = 2; i <= len; ++i)
31     {
32         int maxm = f[i];
33         for (int j = 1; j < i; ++j)
34         {
35             if (Right[j] <= Left[i])
36                 maxm = max(maxm, f[j] + f[i]);
37         }
38         f[i] = maxm;
39     }
40     cout << *max_element(&f[1], &f[len + 1]);//求最大值
41 }

 

拦截导弹

题目描述:某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.

输入描述:输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)

输出描述:输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统.

题目分析:暂时先不发,让我自己理一理思路.

AC代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     int height[55], len = 0, f[55];
 9     while (scanf("%d", &height[++len]) != EOF);
10     f[1] = 1;
11     for (int i = 2; i <= len; ++i)
12     {
13         int maxm = 1;
14         for (int j = 1; j < i; ++j)
15         {
16             if (height[j] >= height[i])
17                 maxm = max(maxm, f[j] + 1);
18         }
19         f[i] = maxm;
20     }
21     cout << *max_element(&f[1], &f[len]) << endl;
22     for (int i = 2; i <= len; ++i)
23     {
24         int maxm = 1;
25         for (int j = 1; j < i; ++j)
26         {
27             if (height[j] < height[i])
28                 maxm = max(maxm, f[j] + 1);
29         }
30         f[i] = maxm;
31     }
32     cout << *max_element(&f[1], &f[len]) << endl;
33     return 0;
34 }

非常抱歉,最后一题的分析我自己还说不出来,请允许我再理一理思路.如果有什么不合理之处,敬请批评指正.

转载于:https://www.cnblogs.com/zkx06111/p/3280564.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值