动态规划进阶篇

来源:互联网 发布:联通软件研究院 亦庄 编辑:程序博客网 时间:2024/05/29 15:59

动态规划进阶篇

如果你已经熟悉了动态规划的基础知识,那么接下来我们来讨论二维的动态规划吧!

问题:
给定一个N×M的表格,每个格子上放着一个苹果。如果你从最左上角的格子上开始走,每次只能向下或者向右走,每到一个格子你就把苹果收集起来。这样下去,你最多能够收集多杀个苹果??

解决这个问题与其它的DP问题并没有其它的区别。
首先,我们要找到问题的“状态”。我们要注意的是我们最多有两种状态去收集苹果:从左边(除了第一列)和从右边(除了第一行)。因此为了求出到达当前格子后最多能收集到多少个苹果, 我们就要先去考察那些能到达当前这个格子的格子,到达它们最多能收集到多少个苹果。 (是不是有点绕,但这句话的本质其实是DP的关键:欲求问题的解,先要去求子问题的解)

经过上面的分析,很容易得出问题的状态S[i][j]表示到达格子(i, j)最多能收集到苹果的个数。那么状态转换方程为:
S[i][j]=A[i][j]+max(S[i1][j],if i>0;S[i][j1],if j>0)
其中 i 代表行,j 代表列。A[i][j] 代表格子(i,j)处苹果的数量。
S[i][j]的计算:1.对于每一行,从左向右计算,然后从上到下逐行处理;2. 对于每一列,从上到下计算,然后从左向右逐列处理。

伪代码:

For i = 0 to N - 1   For j = 0 to M - 1   S[i][j] = A[i][j] +      max(S[i][j-1], if j>0 ; S[i-1][j], if i>0 ; 0)Output S[n-1][m-1]

LintCode 数字三角形

首先来一道简单的题,练练手。

题目描述

给定一个数字三角形,找到从顶部到底部的最小路径和。每一步可以移动到下面一行的相邻数字上。样例比如,给出下列数字三角形:[     [2],    [3,4],   [6,5,7],  [4,1,8,3]]从顶到底部的最小路径和为11 ( 2 + 3 + 5 + 1 = 11)。

对于动态规划问题,首先我们要找到问题的“状态”和“状态转换方程”。
对于该问题我们假设dp[i][j]为从第1行元素到第i行第j个元素的最小路径和。比如说dp[3][2]=5+min(2+3,2+4)=5+min(dp[2][1],dp[2][2]), res[i]为到第i行的最小路径和。则有res[i]=min(dp[i][j]forj=0:dp[i].size());
因为到达元素(i, j)只能从上面相邻的元素到达,因此有:
dp[i][j]=A[i][j]+min(dp[i1][j1] if i>0 and j>0,dp[i1][j] if i>0)
注意:下标从0开始

Talk is cheap, show me thecode!

int minimumTotal(vector<vector<int> > &triangle){    int n = triangle.size();    //行数    vector<vector<int>> dp(n); // 创建一个N行的二维数组    dp[0].push_back(triangle[0][0]);    // 从第二行元素开始,因为第一行只有一个元素    for (int i = 1; i < n; ++i) {        int col = triangle[i].size(); //第i行的元素个数。。。其实 col = i+1;        vector<int> tmp(col, 0);        for (int j = 0; j < col; ++j) {            // 注意边界条件            if (j == 0)                tmp[j] = triangle[i][j] + dp[i-1][j];            else if (j == col -1 )                tmp[j] = triangle[i][j] + dp[i-1][j-1];            else                tmp[j] = triangle[i][j] + min(dp[i-1][j], dp[i-1][j-1]);        }        dp[i] = tmp;    }    int res = dp[n-1][0];    for (int i = 1; i < dp[n-1].size(); ++i)    {        res = min(res,dp[n-1][i]);    }    return res;}

TopCoder AvoidRoads

Problem Statement

In the city, roads are arranged in a grid pattern. Each point on the grid represents a corner where two blocks meet. The points are connected by line segments which represent the various street blocks. Using the cartesian coordinate system, we can assign a pair of integers to each corner as shown below.

这里写图片描述

You are standing at the corner with coordinates 0,0. Your destination is at corner width,height. You will return the number of distinct paths that lead to your destination. Each path must use exactly width+height blocks. In addition, the city has declared certain street blocks untraversable. These blocks may not be a part of any path. You will be given a String[] bad describing which blocks are bad. If (quotes for clarity) “a b c d” is an element of bad, it means the block from corner a,b to corner c,d is untraversable. For example, let’s say
width = 6
length = 6
bad = {“0 0 0 1”,”6 6 5 6”}
The picture below shows the grid, with untraversable blocks darkened in black. A sample path has been highlighted in red.

这里写图片描述
Constraints
- width will be between 1 and 100 inclusive.
- height will be between 1 and 100 inclusive.
- bad will contain between 0 and 50 elements inclusive.
- Each element of bad will contain between 7 and 14 characters inclusive.
- Each element of the bad will be in the format “a b c d” where,
a,b,c,d are integers with no extra leading zeros,
a and c are between 0 and width inclusive,
b and d are between 0 and height inclusive,
and a,b is one block away from c,d.
- The return value will be between 0 and 2^63-1 inclusive.

Examples
0)
6
6
{“0 0 0 1”,”6 6 5 6”}
Returns: 252
Example from above.

1)
1
1
{}
Returns: 2
Four blocks aranged in a square. Only 2 paths allowed.

2)
35
31
{}
Returns: 6406484391866534976
Big number.

3)
2
2
{“0 0 1 0”, “1 2 2 2”, “1 1 2 1”}
Returns: 0

先来看一下题目说了啥吧。给定一个width×height的网格,从坐标(0, 0)沿着网格线走到坐标(width, height)一共有多少种走法。其中一些网格线是走不通的,走不通的网格线以字符串坐标的形式给出。
注意:“Each path must use exactly width+height blocks”, 因此给定一个坐标到下一个坐标只能往右或者往上走。

动态规划问题的解法只有两个步奏:找到问题的“状态”和“状态转换方程”。
对于这个问题如果说所有的网格线都是可以走的通的话,那么个问题与上面收集苹果的问题,有木有感觉解法是一样的!!!!
首先,我们要知道问题的状态:假设从坐标(0,0)走到左边(i, j)一共有dp(i,j)中走法。
因为走到坐标(i,j)处,只有两种方式从左边(i1,j) 或者从下面(i,j1),如果线路是通的话。因此,状态转换方程为

dp[i][j]+={dp[i1[j],dp[i][j1],if i>0 and from (i-1, j) to (i, j) not badif j>0 and from (i, j-1) to (i, j) not bad
因此,现在关键的问题变成判断(i1,j)(i,j1)(i,j)是否是通的。现在提供一种比较简答的方法。将坐标(i1,j),(i,j)转换成字符串,然后在判断该字符串是否在给定的bad string里。
Talk is cheap, show me the code!

#include<iostream>#include<string>#include<vector>#include<algorithm>using namespace std;//将坐标解析成字符串string parse(int x, int y, int i, int j) {    string str;    str = string(1,x+'0') + " " + string(1, y+'0') + " ";    str += string(1,i+'0') + " " + string(1, j+'0');    return str;}/* brief: 判断从坐标的左边(x-1, y)或者下面(x, y-1)是否可以到达坐标(x, y)*param x, y :坐标(x, y)*param bad: 不通的路径*param str: 取"Left"表示判断从(x-1, y)到(x,y)是否为通。"Down"表示从下面**/bool canReach(int x, int y, vector<string> &bad, string str){    int i = x, j = y;    if (str == "Left") i = x-1;    if (str == "Down") j = y - 1;    auto iter = find(bad.begin(), bad.end(), parse(i, j, x, y));    if (iter != bad.end()) {    //说明该路径不可达        // 该路径我们已经使用过了,则我们可以删除提高查找效率        bad.erase(iter);          return false;    }    //注意题目中的陷阱    else if ((iter = find(bad.begin(), bad.end(),                          parse(x, y,i,j))) != bad.end()) {        bad.erase(iter);        return false;    }    return true;}/** brief: 判断从坐标(0,0)到坐标(width, height)有多少种路径*param bad: 不同的路径坐标*/long long numWays(int width, int height, vector<string> bad){    // 创建一个二维数组并且初始化为0    vector<vector<long long>> dp(width+1, vector<long long>(height+1, 0));    dp[0][0] = 1; //边界条件    for (int i = 0; i <= width; ++i) {        for (int j = 0; j <= height; ++j) {            if (i > 0 && canReach(i, j, bad, "Left"))                dp[i][j] += dp[i-1][j];            if (j > 0 && canReach(i, j, bad, "Down"))                dp[i][j] += dp[i][j-1];        }    }    return dp[width][height];}int main(){    vector<string> bad {"0 0 0 1","6 6 5 6"};    cout << numWays(6, 6, bad) << endl;    return 0;}

Reference:
[1] 动态规划:从新手到专家
[2] Dynamic Programming – From Novice to Advanced

0 0