动态规划4.1——基础动态规划问题

题目上添加了超链接,大家点一下题目就会自动跳转到Poj原题界面~~              冲鸭冲鸭ヾ(◍°∇°◍)ノ゙。

预警、预警本章节开始,再次劝退!

但我们也自此才开始接触到算法的精髓可以说之前所学习的方法都是我们的解题手段,但自此章节开始,我们所应对的变成了完整的综合题。动态规划问题类型很广,在算法问题中占比很大。简单有白给模板题,难起来可以难的没边。

在应用动态规划方法解决问题时,关键是确定当前问题的所有子问题的最优子结构,再构造出原问题的解,也就是多阶段决策中的转移方程转移方程的通常具有这样的特点:当前阶段的值与所有之前阶段值的最优值关联。动态规划所求为问题全局最优解。

动态规划题目特点:有重叠的子问题

1:计数

      —有多少种方式走到右下角

      —有多少种方法选出k个数使得和为Sum

2:求最大最小值

      —从左上角走到右下角路径的最大数字和

      —最长上升子序列长度

3:求存在性

      —取石子游戏,先手是否必胜

      —能不能选出k个数使得和是Sum

 

动态规划组成部分:

1:确定状态

      —确定最后一步(最优策略)

      —抽象子问题

2:归纳转移方程

3:初始条件和边界情况

4:计算顺序

 

4.1.1 The Triangle (1163/3176)

题意:一个数字三角形如图4.1所示,从顶部出发,每一个结点可以选择向左或向右走,要求找到一个从上到下的路径,路径上数字的和最大。

小笔记:简单入门题,需要注意思想,从这里出发理解动态规划。

#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    int D[355][355]; //存储到该点的最大路径
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        for (int j = 0; j <= i; j++)
            scanf("%d", &D[i][j]);
    for (int i = n - 2; i >= 0; i--)
        for (int j = 0; j <= i; j++)
            D[i][j] += max(D[i + 1][j], D[i + 1][j + 1]);
    printf("%d\n", D[0][0]);
    return 0;
}

  

4.1.2 Brackets (2955)

题意:给出一个括号序列,找出最长的规范括号子序列。

小笔记:区间dp、记忆化搜索

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 101;
//如果a,b为左右成对的括号,则返回长度2,否则返回0。
int f(char a, char b)
{
    return ((a == '[' && b == ']') || (a == '(' && b == ')')) ? 2 : 0;
}
int main()
{
    char s[N]; //括号序列
    while (scanf("%s", s) && s[0] != 'e')
    {
        int D[N][N] = {0};               //记录s[i]~s[j]之间规范括号子序列的长度
        int m = strlen(s);               //m为区间总长度
        for (int t = 1; t <= m - 1; t++) //t为子区间长度
            for (int i = 0; i <= m - t - 1; i++)
            {
                int j = i + t;
                for (int k = i; k <= j - 1; k++)
                    D[i][j] = max(D[i][j], max(D[i][k] + D[k + 1][j], D[i + 1][j - 1] + f(s[i], s[j])));
            }
        printf("%d\n", D[0][m - 1]);
    }
    return 0;
}

  

4.1.3 Multiplication Puzzle (1651)

题意:n个数排成一排,从中间位置取走一个数,和它前后两个数相乘得到一个乘积值,再取一个数,继续计算,直到剩下的最后三个数相乘,将所有乘积相加,求这个和的最小值。

小笔记:区间dp、矩阵连乘问题

 题解:用a[1…n]保存n个数,D[i,j]为i到j区间计算出来的和的最小值,区间长度s=j-i,在区间内取一个位置k,则D[i,j]为所有D[i,k]+D[k,j]+a[i]*a[k]*a[j]的最小值。

#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
const int N = 105;
int main()
{
    int n;
    int a[N];
    int D[N][N] = {0}; //D[i,j]为i到j区间计算出来的和的最小值
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int s = 2; s <= n - 1; s++) //s为区间长度
        for (int i = 1; i <= n - s; i++)
        {
            int j = i + s;
            D[i][j] = INT_MAX;
            for (int k = i + 1; k < j; k++) //k为区间内某位置
                D[i][j] = min(D[i][j], D[i][k] + D[k][j] + a[i] * a[k] * a[j]);
        }
    printf("%d\n", D[1][n]);
    return 0;
}

  

4.1.4 Apple Catching (2385)

题意:两个苹果树1和2,在t分钟内每分钟有1棵树向下落1个苹果,在树下接苹果,初始位置在1号树下,换一棵树记作交换1次位置,最多可换w次位置,计算最多可以接多少苹果。

小笔记:经典dp,不难,注意细节。

#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
    int t, w;
    scanf("%d%d", &t, &w);
    int D[35] = {0};
    while (t--)
    {
        int x;
        scanf("%d", &x);
        D[0] += x % 2; //每个时间对D[0]进行更新
        for (int i = 1; i <= w; i++)
            D[i] = max(D[i], D[i - 1]) + (i + x) % 2;
    }
    printf("%d\n", D[w]);
    return 0;
}

  

4.1.5 Hamming Problem (2545)

题意:定义一个递增序列,序列中的数字是只包含p1、p2、p3三个素数因子的所有自然数,输出序列中的第i个数。

题解:简单题。用数组h保存结果,用x1、x2、x3分别保存h中的数字与p1、p2、p3相乘的结果,由序列的属性可知,x1、x2、x3这3个值都属于h并且都是从1开始依次递增。用k1、k2、k3记录x1、x2、x3在h中取的值的位置,当被“使用”过之后,对应的位置加1。

h在求解的过程中依赖于之前子问题的解,并取一个最小值作为当前值,也是动态规划思想的体现。

#include <cstdio>
#include <algorithm>
using namespace std;
long long h[1000000];
int main()
{
    int p1, p2, p3, i;
    while (~scanf("%d%d%d%d", &p1, &p2, &p3, &i))
    {
        h[0] = 1;
        int k1, k2, k3;
        k1 = k2 = k3 = 0;
        for (int k = 1; k <= i; k++)
        {
            long long x1 = h[k1] * p1;
            long long x2 = h[k2] * p2;
            long long x3 = h[k3] * p3;
            h[k] = min(min(x1, x2), x3);
            if (h[k] == x1)
                k1++;
            if (h[k] == x2)
                k2++;
            if (h[k] == x3)
                k3++;
        }
        printf("%lld\n", h[i]);
    }
    return 0;
}

  

posted @ 2021-06-05 16:22  anyiya  阅读(48)  评论(0编辑  收藏  举报