简单动态规划(scauacm 17996 Daily Cool Run)

1、什么是动态规划?

动态规划的定义,引自维基百科:

dynamicprogramming is a method for solving a complex problem bybreakingit down into a collection of simpler subproblems.

动态规划是对某类最优化问题的解决方法。动态规划寻找一种对问题的观察角度,构造出问题的子问题,使原问题能够以递推的方式去解决。


2、动态规划原理

适合应用动态规划方法求解的最优化问题应该具备两个要素:最优子结构子问题重叠


下面由”求一个数列的最长上升(递增)子数列长度(LIS)”问题来说明:


给定一个长度为6的数列:1 5 6 2 7 8

这个数列的的LIS1 5 6 7 8,长度为5

1 2 7 85 6 7 8同样为数列的子上升数列,但它们都不是最长上升子数列。


最优子结构

用动态规划方法求解最优化问题的第一步是刻画出最优解的结构。如果一个问题的最优解包含其子问题的最优解,就称此问题具有最优子结构性质。


重新定义上述问题:

给定一个长度为N的数列;

Fk为:以数列第K项结尾的LIS长度

则F1~N中的最大值即为所求

对于求解Fk,每个F1~k-1都是Fk的子问题。不难证明,以k项结尾的LIS包含着以1k-1项结尾的LIS


需要注意的是,定义最优子结构有个隐含条件,就是子结构的无后效性。从F1_k-1可以求得Fk,也就是每个阶段的最优解可以从之前某个阶段的最优解直接得到,则称这个问题具有最优子结构。

而同时,不管之前某个阶段的最优解是如何得到的,不管F1~k-1是如何得到的,因为要求Fk时,只需知道F1~k-1的结果,而并不关心它是如何求出来的。


子问题重叠

重叠的子问题实际上是同一个问题,只是作为不同问题的子问题出现。通过记录这些重叠的子问题,避免问题的反复求解,从而达到以空间换时间的效果。


3、动态规划的具体实现


题意:

天天酷跑,给一个2*n的格子,初始时在(21)这个点上,每次两种操作

从(2i)移动到(2i+1

从(2i)跳起,经过(1i+1)(1i+2)跳到(2i+3

当到达(1n)或者(2n)的时候,游戏结束,每个格子上有一个分数,游戏总分数就是经过的格子分数之和,求能获得的分数的最大值


Sample

0 0 1 1 0

2 1 2 0 1答案6


思路:

DP[i]表示,到第i个格子而且在地面时能获得的最高分数,答案就是DP[n]

状态转移:

1.i-1个格子直接走过来dp[i]← dp[i-1] + score[2][i]

2.i-3个格子跳过来dp[i]← dp[i-3] + score[1][i-2] + score[1][i-1] + score[2][i]





自顶向下,通过递归实现



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int a[2][100005];
int d[100005];
int vis[100005];
int dp(int i)
{
    if(vis[i]) return d[i];
    d[i] = a[1][i];
    if(i == 0)
        return a[1][0];
    if(i <= 2)
        d[i] += dp(i-1);
    else
    {
        d[i] += max(dp(i-1) , dp(i-3) + a[0][i-1] + a[0][i-2]);
    }
    vis[i] = 1;
    return d[i];
}

int main()
{
    int T, n;
    scanf("%d", &T);
    while(T--)
    {
        memset(a, 0, sizeof(a));
        scanf("%d", &n);
        for(int i=0; i<n; i++)
            scanf("%d", &a[0][i]);
        for(int i=0; i<n; i++)
            scanf("%d", &a[1][i]);
        memset(d, 0, sizeof(d));
        memset(vis, 0, sizeof(vis));
        int t;
        if(n==3)
        {
            t = max(a[0][2]+a[0][1]+a[1][0], a[1][2]+a[1][1]+a[1][0]);
        }
        else if(n==2)
        {
            t = max(a[0][1]+a[1][0], a[1][1]+a[1][0]);
        }
        else if(n == 1)
        {
            t = a[1][0];
        }
        else if(n >3)
        {
            t = max(dp(n-1), dp(n-3) + a[0][n-1] + a[0][n-2]);
            t = max(t, dp(n-2) + a[0][n-1]);
        }
        printf("%d\n", t);
    }
    return 0;
}




自底向上,通过递推实现:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

#define N 200005

int dp[N], a[2][N];

int main() {
    int n, t;
    cin >> t;
    while (t--) {
        cin >> n;
        memset(a, 0, sizeof(a));
        for (int j = 0; j < 2; j++) for (int i = 1; i <= n; i++) scanf("%d", &a[j][i]);
        dp[0] = 0;
        for (int i = 1; i <= n + 2; i++) {
            dp[i] = dp[i - 1] + a[1][i];
            if (i > 3) dp[i] = max(dp[i], dp[i - 3] + a[0][i - 2] + a[0][i - 1] + a[1][i]);
        }
        printf("%d\n", dp[n + 2]);
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章