动态规划入门(一)

2017-09-01 11:29:43

writer:pprp

看sprout台湾大学acm教学视频的第一部分:

里边涉及到四道小例题

感觉很好就拿来写了写:

题意还有代码说明都在代码中:

1、最基础的骨牌问题:

/*
@param:dp 入门
@writer:pprp
@declare:最经典最简单的dp
@begin:9:00
@end:10:00
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

//未优化的最基础的
int fun(int n)
{
    if(n == 1) return 1;
    if(n == 2) return 2;

    return fun(n-1) + fun(n-2);
}

//自顶向下的记忆化
//top down 小心递回过深
int dp[101] = {1,1,2};
int fun2(int n)
{
    if(dp[n] != 0)
        return dp[n];
    return fun2(n-1) + fun(n-2);
}

//自底向上
//子问题一定要比母问题要先跑到,注意递回跑法
int dp2[101] = {0};
void build()
{
    dp2[1] = 1;
    dp2[2] = 2;
    for(int i = 3 ; i < 101; i++)
     dp2[i] = dp2[i-1] + dp2[i-2];
}
int fun3(int n)
{
    return dp2[n];
}

int main()
{
    build();
    int a;
    while(cin >> a)
    {
        cout << fun(a) << endl;
        cout << fun2(a) << endl;
        cout << fun3(a) << endl;
    }

    return 0;
}

2、涂色问题:题意见代码头

/*
@param:dp 入门
@writer:pprp
@declare:最经典最简单的dp,给红绿蓝三种颜色,让你求出蓝绿不相邻的排列n个元素的情况
@begin:10:10
@end:10:28
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
状态确定:按照最后一位的颜色确定状态
f(n,0):最后一位是红色的数量
f(n,1):最后一位是绿色的数量
f(n,2):最后一位是蓝色的数量
状态转移:
f(n,0) = f(n-1,0) + f(n-1,1) + f(n-1,2)
f(n,1) = f(n-1,0) + f(n-1,1)
f(n,2) = f(n-1,0) + f(n-1,2)
初始条件:
f(1,0) = 1;
f(1,1) = 1;
f(1,2) = 1;
最终答案:
f(n,0) + f(n,1) +f(n,2);
*/

// top down
int dp1[101][3] = {0};

void init()
{
    dp1[1][0] = dp1[1][1] = dp1[1][2] = 1;
}

int fun(int n,int m)
{
    if(dp1[n][m] != 0)
        return dp1[n][m];

    if(m == 0) dp1[n][0] = fun(n-1,0) + fun(n-1,1) + fun(n-1,2);
    if(m == 1) dp1[n][1] = fun(n-1,0) + fun(n-1,1);
    if(m == 2) dp1[n][2] = fun(n-1,0) + fun(n-1,2);

    return dp1[n][m];
}

//bottom up
int dp2[101][3] = {3};
int build()
{
    dp2[1][0] = dp2[1][1] = dp2[1][2] = 1;
    for(int i = 2; i < 101 ; i++)
    {
        dp2[i][0] = dp2[i-1][0] + dp2[i-1][1] + dp2[i-1][2];
        dp2[i][1] = dp2[i-1][0] + dp2[i-1][1];
        dp2[i][2] = dp2[i-1][0] + dp2[i-1][2];
    }
}
int fun2(int n)
{
    return dp2[n][0] + dp2[n][1] + dp2[n][2];
}

//run
int main()
{
    int a;
    cin >> a;
    init();
    build();

    cout << fun(a,0) + fun(a,1) + fun(a,2) << endl;
    cout << fun2(a) << endl;
    return 0;
}

3、骨牌问题2,加了一个L型骨牌

/*
@param:dp 入门
@writer:pprp
@declare:最经典最简单的dp, 给你2*1 & 3*1 L型骨牌填满2*n的格子,有几种拍法
@begin:10:36
@end:11:00
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
状态分析:
f(n) 代表的是放n*2个格子时候的方法数
状态转移:
如果最后那块放2*1的:f(n) = f(n-1) + f(n-2)
如果最后那块放3*1的:f(n) = f(n-3) + f(n-4) + ... + f(0),由于对称的关系,要乘2
综合起来: f(n) = f(n-1) + f(n-2) + 2 * (f(n-3) + f(n-4) +...+ f(0))
边界状态:f(0) = 1; f(1) = 1; f(2) = 2;
*/

//top down
int dp[101] = {0};
void init()
{
    dp[0] = dp[1] = 1;
    dp[2] = 2;
}
int fun(int n)
{
    init();
    if(dp[n] != 0) return dp[n];
    dp[n] = 2 * fun(n-1) + fun(n-3);
    return dp[n];
}

//bottom up
int dp2[101] = {};

void build()
{
    dp2[0] = dp2[1] = 1;
    dp2[2] = 2;

    for(int i = 3 ; i < 101 ; i++)
        dp2[i] = 2 * dp2[i-1] + dp2[i-3];
}

int fun2(int n)
{
    return dp2[n];
}

int main()
{
    int a;
    srand((int)time(NULL));
    a = rand()%100;

    build();
    cout << fun(a) << endl;
    cout << fun2(a) << endl;

    return 0;
}

4.找到不相邻的数的最大和

/*
@param:dp 入门
@writer:pprp
@declare:最经典最简单的dp,给你一个正整数阵列,
从里边取出不相邻的数,问你取出数字和最大为多少?
@begin:1:05
@end:11:25
@error:应该是从1开始不是从0开始
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
状态分析:
f(n)代表的是当前取了这个位置以后的最大数字和
状态转移:
f(n) = max(f(n-2),f(n-3) + arr[n];
边界状态:f(0) = 0, f(1) = arr[1], f[2] = max(arr[1],arr[2])
*/

//top down
int arr[101] = {};
int dp[110] = {};

void init()
{
    dp[0] = 0;
    dp[1] = arr[1];
    dp[2] = max(arr[1],arr[2]);
    dp[3] = max(arr[1]+arr[3],arr[2]);
}

int fun(int n)
{
    init();
    if(dp[n] != 0)
        return dp[n];
    dp[n] = max(fun(n-2),fun(n-3)) + arr[n];
    return dp[n];
}

int dp2[110] = {};
//bottom up
void build()
{
    dp2[0] = 0;
    dp2[1] = arr[1];
    dp2[2] = max(arr[1],arr[2]);

    for(int i = 3; i < 101 ; i++)
    {
        dp2[i] = max(dp2[i-2],dp2[i-3]) + arr[i];
    }
}

int fun2(int n)
{
    return dp2[n];
}
int main()
{
    freopen("in.txt","r",stdin);
    int n;
    cin >> n;
    for(int i = 1 ; i <= n ; i++)
        cin >> arr[i];

    init();
    build();

    cout << max(fun(4),fun(5)) << endl;
    cout << max(fun2(4),fun2(5)) << endl;
    return 0;
}

总结:

状态确定很重要,利用对称的关系分析可以简化,

动态规划是一个走一步算一步的算法过程,不要全局的去分析,否则越分析越乱,按照每一步的走向来确定状态转移方程

做的多了动态规划才能有灵感

时间: 2024-12-30 02:37:48

动态规划入门(一)的相关文章

动态规划入门

通过金矿模型介绍动态规划 点击下载01背包测试数据.rar 对于动态规划,每个刚接触的人都需要一段时间来理解,特别是第一次接触的时候总是想不通为什么这种方法可行,这篇文章就是为了帮助大家理解动态规划,并通过讲解基本的01背包问题来引导读者如何去思考动态规划.本文力求通俗易懂,无异性,不让读者感到迷惑,引导读者去思考,所以如果你在阅读中发现有不通顺的地方,让你产生错误理解的地方,让你难得读懂的地方,请跟贴指出,谢谢! ----第一节----初识动态规划-------- 经典的01背包问题是这样的:

[转]很特别的一个动态规划入门教程

很特别的一个动态规划入门教程 今天在网上看到一个讲动态规划的文章,是以01背包为例的,这文章和书上的讲解非常不一样,令我眼前一亮,于是转载一下下--- (说明一下,本人非常痛恨教材公式定理漫天飞,实际的讲解却讲得非常枯涩难懂,这种中国式的教育已经延绵了几千年了,现在中国的教材还是这个样子,讲清楚 些明白些就那么难么?高中有个老师讲的一句话一直觉得很有道理:“教得会天才不是真本事,能把博士生的东西讲到小学生都会用那才是真水平.”) 附上原文地址: http://www.cnblogs.com/sd

很特别的一个动态规划入门教程

很特别的一个动态规划入门教程 (2016-03-10 17:05:48) 转载▼ 标签: cpp 动态规划 分类: Cpp精选 很特别的一个动态规划入门教程 今天在网上看到一个讲动态规划的文章,是以01背包为例的,这文章和书上的讲解非常不一样,令我眼前一亮,于是转载一下下---(说明一下,本人非常痛恨教材公式定理漫天飞,实际的讲解却讲得非常枯涩难懂,这种中国式的教育已经延绵了几千年了,现在中国的教材还是这个样子,讲清楚些明白些就那么难么?高中有个老师讲的一句话一直觉得很有道理:“教得会天才不是真

动态规划入门戳进来

学动态规划自然要从数字三角形开始起步,那么我们就先从数字三角形开始. 数字三角形题目:有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外的每个数的左下方和右下方各有一个数,如下图所示: 1 3 2 4 10 1 4 3 2 20 从第一行的数开始,每次可以往下或往右下走一格,直到走到最下行,把沿途经过的数全部加起来.如何走才能使这个和最大? 知道回溯法么(请参看:八皇后与回溯法),你会发现这是一个动态的决策问题:每次有两种选择--向左或是向右,每一步决策又影响到后面的决策,如果用贪心

由LCS到编辑距离—动态规划入门—算法学习笔记

一切计算机问题,解决方法可以归结为两类:分治和封装.分治是减层,封装是加层. 动态规划问题同样可以用这种思路,分治. 它可以划分为多个子问题解决,那这样是不是用简单的递归就完成了?也许是的,但是这样会涉及太多的不便的操作.因为子问题有重叠! 针对这种子问题有重叠的情况的解决,就是提高效率的关键. 所以动态规划问题可以总结为:最优子结构和重叠子问题. 解决这个子问题的方式的关键就是:memoization,备忘录. 动态规划算法分以下4个步骤: 描述最优解的结构 递归定义最优解的值 按自底向上的方

hdu2084动态规划入门题----数塔

原题:数塔 这个是动态规划入门题,比较简单. 题意是: 一个数字组成的三角形,从上到下找一条路径,使这条路径上数字之和最大. 解题思路,就是要从下往上看.举个例子: 如果你从上到下走到了第4行第1个数,也就是2,那么接下来有两个数可以走19和7,而你必然会选择19. 所以就可以根据这个思路更新上面一行的数.把2更新成2+19=21.18更新成18+10=28,9更新成9+10=19,5更新成5+16=21 重复上面的思路最后第一行累加出来的就是最大值了. 思路很简单,最简单的实现就是你也开一个二

白话算法之【动态规划入门】

什么是动态规划? 动态规划(Dynamic Programming,所以我们简称动态规划为DP)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法.20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新

动态规划——入门篇

动态规划相信大家都知道,动态规划算法也是新手在刚接触算法设计时很苦恼的问题,有时候觉得难以理解,但是真正理解之后,就会觉得动态规划其实并没有想象中那么难.网上也有很多关于讲解动态规划的文章,大多都是叙述概念,讲解原理,让人觉得晦涩难懂,即使一时间看懂了,发现当自己做题的时候又会觉得无所适从.我觉得,理解算法最重要的还是在于练习,只有通过自己练习,才可以更快地提升.话不多说,接下来,下面我就通过一个例子来一步一步讲解动态规划是怎样使用的,只有知道怎样使用,才能更好地理解,而不是一味地对概念和原理进

动态规划入门-01背包问题 - poj3624

2017-08-12 18:50:13 writer:pprp 对于最基础的动态规划01背包问题,都花了我好长时间去理解: poj3624是一个最基本的01背包问题: 题意:给你N个物品,给你一个容量为M的背包 给你每个物品的重量,Wi 给你每个物品的价值,Di 求解在该容量下的物品最高价值? 分析: 状态: dp[i][j] = a 剩下i件 当前容量为j的情况下的最大价值为a 如果用 i 来枚举物品编号, 用 j 来枚举重量,那么 if ( j is from 1 to weight[i]