• 算法设计与分析_练习三_动态规划算法


    目录

    01:最长单调递增子序列

    02:点菜

    03:合唱队形

    04:数塔问题

    05:最大子段和

    06:最长公共子序列


    01:最长单调递增子序列

    描述

    一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).

    你的任务,就是对于给定的序列,求出最长上升子序列的长度。

    输入

    输入两行.第一行输入n(n<=1000)的值,第二行输入n各数

    输出

    最大上升子序列的长度

    样例输入

    8
    65 158 170 155 239 300 207 389

    样例输出

    6

    1. #include
    2. using namespace std;
    3. #define NUM 100
    4. int a[NUM]; //序列L
    5. int LIS(int n) {
    6. int b[NUM] = {0}; //辅助数组b
    7. int i, j;
    8. b[1] = 1; //以a[1]结尾的子序列中只包含一个元素
    9. int max = 0; //数组b的最大值
    10. for (i = 2; i <= n; i++) {
    11. int k = 0;
    12. for (j = 1; j < i; j++) { //0~i-1之间,b的最大值
    13. if (a[j] <= a[i] && k < b[j]) k = b[j];
    14. }
    15. b[i] = k + 1;
    16. if (max < b[i]) {
    17. max = b[i];
    18. }
    19. }
    20. return max;
    21. }
    22. int main() {
    23. int n, i;
    24. cin >> n;
    25. for(i = 1; i <= n; i++) {
    26. cin >> a[i];
    27. }
    28. cout << LIS(n);
    29. return 0;
    30. }

     

    02:点菜

    描述

    题目描述:umi拿到了uoi的镭牌后,立刻拉着基友小A到了一家餐馆,很低端的那种。uim指着墙上的价目表(太低级了没有菜单),说:“随便点”。不过uim由于买了一些书,口袋里只剩M元(M≤10000)。餐馆虽低端,但是菜品种类不少,有N种(N≤100),第i种卖ai元(ai ≤1000)。由于是很低端的餐馆,所以每种菜只有一份。

    小A奉行“不把钱吃光不罢休”,所以他点单一定刚好把uim身上所有钱花完。他想知道有多少种点菜方法。

    输入

    第一行是两个数字,表示N和M。

    第二行起N个正数ai(可以有相同的数字,每个数字均在10001000以内)。

    输出

    一个正整数,表示点菜方案数,保证答案的范围在int之内。

    样例输入

    4 4
    1 1 2 2

    样例输出

    3

    1. #include
    2. using namespace std;
    3. int f[101][1001], v[101]; //f[][]为方法数
    4. int main() {
    5. int n, m;
    6. cin >> n >> m;
    7. for (int i = 1; i <= n; i++)
    8. cin >> v[i]; // 输入每种菜的价格
    9. for (int i = 1; i <= n; i++) {
    10. for (int j = 0; j <= m; j++) {
    11. //j元买不了这个菜,不买,则前i种菜花费j元的方法=前i-1种菜j花费j元的方法
    12. if (j < v[i]) f[i][j] = f[i - 1][j];
    13. //j元刚好买这个菜,则前i种菜花费j元的方法=前i-1种菜花费j元的方法加上只买这一个菜的方法(1种)
    14. if (j == v[i]) f[i][j] = f[i - 1][j] + 1;
    15. //j元买这个菜还有剩余,则f[i][j]为前i-1种菜花费j元的方法加上前i-1种菜花费j-v[i](因为还要买这个菜)的方法
    16. if (j > v[i]) f[i][j] = f[i - 1][j] + f[i - 1][j - v[i]];
    17. }
    18. }
    19. cout << f[n][m];
    20. return 0;
    21. }

     

    03:合唱队形

    描述

    N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,  则他们的身高满足T1<...Ti+1>…>TK(1<=i<=K)。已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

    输入

    输入的第一行是一个整数N(2<=N<=100),表示同学的总数。 第二有N个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。

    输出

    输出最少需要几位同学出列。

    样例输入

    8
    186 186 150 200 160 130 197 220

    样例输出

    4

    1. #include
    2. using namespace std;
    3. int n, a[110], f[110][2], MAX; //f[i][1]表示上升子序列的个数,f[i][2]表示下降子序列的个数
    4. int main() {
    5. cin >> n;
    6. for(int i = 1; i <= n; i++) {
    7. cin >> a[i];
    8. int mx = 0;
    9. for(int j = 1; j < i; j++) {
    10. if(a[j] < a[i]) {
    11. mx = max(mx, f[j][1]); //求上升子序列的最大值,也就是第一部分可以留下的最大人数
    12. }
    13. }
    14. f[i][1] = mx + 1;
    15. }
    16. for(int i = n; i >= 1; i--) {
    17. int mx = 0;
    18. for(int j = n; j > i; j--) {
    19. if(a[j] < a[i]) {
    20. mx = max(mx, f[j][2]); //求下降子序列的最大值,也就是第二部分可以留下的最大人数
    21. }
    22. }
    23. f[i][2] = mx + 1;
    24. //f[i][1]+f[i][2]-1表示当a[i]最高点时留下的人数,这里减一,因为上升和下降的个数都包括了最高点,所以减去一个
    25. MAX = max(MAX, f[i][1] + f[i][2] - 1);
    26. }
    27. cout << n - MAX << endl;
    28. return 0;
    29. }

     

    04:数塔问题

    描述

    设有一个三角形的数塔,顶点为根结点,每个结点有一个整数值。从顶点出发,可以向左走或向右走,如图所示:

    若要求从根结点开始,请找出一条路径,使路径之和最大,只要输出路径的和。

    输入

    第一行为n(n<50),表示数塔的层数
    从第2行至n+1行,每行有若干个数据,表示数塔中的数值。

    输出

    最大的路径值。

    样例输入

    5
    13
    11  8
    12  7  26
    6  14  15  8
    12  7  13  24  11

    样例输出

    86

    1. #include
    2. using namespace std;
    3. #define NUM 100
    4. int tri[NUM][NUM];
    5. int triangle(int n) {
    6. int i, j;
    7. for (i = n - 2; i >= 0; i--) {
    8. for (j = 0; j <= i; j++) {
    9. if( tri[i + 1][j] > tri[i + 1][j + 1]) {
    10. tri[i][j] = tri[i][j] + tri[i + 1][j]; //向左走
    11. } else {
    12. tri[i][j] = tri[i][j] + tri[i + 1][j + 1]; //向右走
    13. }
    14. }
    15. }
    16. return tri[0][0];
    17. }
    18. int main() {
    19. int n, i, j;
    20. cin >> n;
    21. for(i = 0; i < n; i++) {
    22. for(j = 0; j <= i; j++) {
    23. cin >> tri[i][j];
    24. }
    25. }
    26. cout << triangle(n);
    27. }

     

    05:最大子段和

    描述

    问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-20,11,-4,13,-5,-2)时,最大子段和为20。

    输入

    输入n及n个数

    输出

    子段和的最大值

    样例输入

    6
    -20 11 -4 13 -5 -2

    样例输出

    20

    1. #include
    2. using namespace std;
    3. #define NUM 1001
    4. int a[NUM];
    5. int MaxSum(int n, int &besti, int &bestj) {
    6. int sum = 0;
    7. int b = 0;
    8. // 当b[i-1]<=0时,记录b[i]=a[i]的位置
    9. int begin = 0;
    10. for(int i = 1; i <= n; i++) {
    11. if(b > 0) b += a[i];
    12. else {
    13. b = a[i]; // 更新到最新的位置
    14. begin = i;
    15. }
    16. if(b > sum) {
    17. sum = b;
    18. // 得到新的最优值时,更新左右边界
    19. besti = begin;
    20. bestj = i;
    21. }
    22. }
    23. return sum;
    24. }
    25. int main() {
    26. int n, besti, bestj, i;
    27. besti = 0;
    28. bestj = 0;
    29. cin >> n;
    30. for(i = 1; i <= n; i++) {
    31. cin >> a[i];
    32. }
    33. cout << MaxSum(n, besti, bestj);
    34. return 0;
    35. }

     

    06:最长公共子序列

    描述

    若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。

    例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列。

    给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。

    给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列

    输入

    三行.第一行m和n,分别表示两个子序列的长度(符号的个数)
    接下来两行,分别表示X和Y

    输出

    最长公共子序列的长度

    样例输入

    7 6
    ABCBDAB
    BDCABA

    样例输出

    4

    1. #include
    2. using namespace std;
    3. #define NUM 100
    4. int c[NUM][NUM]; // LCS长度的计算,x[1~i]和y[1~j]之间的公共子序列的长度
    5. // int b[NUM][NUM]; // LCS字符的搜索过程,辅助完成最优解的计算
    6. void LCSLength(int m, int n, const char x[], char y[]) {
    7. int i, j;
    8. // 数组c的第0行、第0列置0
    9. for(i = 1; i <= m; i++) c[i][0] = 0;
    10. for(i = 1; i <= 0; i++) c[0][i] = 0;
    11. //根据递推公式构造数组c
    12. for(i = 1; i <= m; i++) {
    13. for(j = 1; j <= n; j++) {
    14. if(x[i] == y[j]) { //↖
    15. c[i][j] = c[i - 1][j - 1] + 1;
    16. // b[i][j] = 1;
    17. } else if(c[i - 1][j] > c[i][j - 1]) { //↑
    18. c[i][j] = c[i - 1][j];
    19. // b[i][j] = 2;
    20. } else { //←
    21. c[i][j] = c[i][j - 1];
    22. // b[i][j] = 3;
    23. }
    24. }
    25. }
    26. }
    27. //void LCS(int i, int j, char x[]) {
    28. // if(i == 0 || j == 0)return;
    29. // if(b[i][j] == 1) {
    30. // LCS(i - 1, j - 1, x);
    31. // cout << x[i] << " ";
    32. // } else if(b[i][j] == 2) LCS(i - 1, j, x);
    33. // else LCS(i, j - 1, x);
    34. //}
    35. int main() {
    36. char x[NUM];
    37. char y[NUM];
    38. int m, n, i;
    39. cin >> m >> n;
    40. for(i = 1; i <= m; i++) {
    41. cin >> x[i];
    42. }
    43. for(i = 1; i <= n; i++) {
    44. cin >> y[i];
    45. }
    46. LCSLength(m, n, x, y);
    47. cout << c[m][n]; // 输出子序列长度
    48. // LCS(m, n, x);
    49. return 0;
    50. }
  • 相关阅读:
    网络传输中的重要参数-谈谈带宽
    基于spring boot的医疗管理系统 /基于java的医疗系统
    20231017定时任务
    微服务守护神-Sentinel-概念
    项目管理中,如何应对需求蔓延?
    MybatisPlus为什么可以不用@MapperScan
    Mavendeploy命令详解
    postman环境变量的设置
    RocketMQ 详解系列
    常见React Hooks 钩子函数用法
  • 原文地址:https://blog.csdn.net/qq_55793988/article/details/127194113