原题来自维基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 }
非常抱歉,最后一题的分析我自己还说不出来,请允许我再理一理思路.如果有什么不合理之处,敬请批评指正.