信奥日记——动态规划(动规初步)

什么是动态规划

动态规划(Dynamic Programming DP 是运筹学的一个分支,是求解决策过程最优化的过程。其在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。

算法核心思想

  • 通过拆分子问题记忆状态,来减少重复计算,将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法,与分治算法类似。
  • 与分治算法不同的是,动态规划问题,分解得到子问题往往不是互相独立的( 即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解 )

问题引入

求斐波那契数列第 n 项的值

斐波那契数列
指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
i 项的值等于第 i-1 项加上第 i-2

Ⅰ.用已知的值求自己

int n, f[10086];
cin >> n;
f[0] = 0; f[1] = 1;		//初始状态
for (int i = 2; i <= n; ++i)
    f[i] = f[i-1] + f[i-2]; 	//状态转移
cout << f[n];

Ⅱ.用自己的值求未知

int n, f[10086];
cin >> n;
f[0] = 0; f[1] = 1;		//初始状态
for (int i = 0; i < n; ++i)
	f[i+1] += f[i]; f[i+2] += f[i];		//状态转移
cout << f[n];

Ⅱ.记忆化搜索

普通的搜索是这样的
在这样的情况下如果是多次询问 那么就会有很多次重复计算

int dfs(int i) {    //求斐波那契数第 i 项的值
    if (i == 0) return 0;
    if (i == 1) return 1;
    return dfs(i-2)+dfs(i-1);
}

这时候如果我们把已经算出来的值记录下来,那么就可以大大降低算法的时间复杂度

int dfs(int i) {	//求斐波那契数第 i 项的值
    if (i == 0) return 0;
    if (i == 1) return 1;
    if (g[i]) return f[i];	//g[i] 代表斐波那契数列第 i 项 有没有被算出来
    g[i] = true;
    f[i] = dfs(i-2) + dfs(i-1);	//f[i] 代表斐波那契数量第 i 项
    return f[i];
}

总结

/**********动态规划(DP)**********
 * 用若干状态储存对应的值的转移方法
 * 1.状态:刻化题目中在变化的量
 * 2.初始化:变化的量最开始的情况
 *   SA:斐波那契数列中的 f[0] = 0 与 f[1] = 1
 * 3.转移方程:描述这些量怎么变化
 *   SA:f[i] = f[i-1]+f[i-2]
 *******************************/

例题

过河卒

传送门
在这里插入图片描述

题解

首先我先假设没有马的影响,那么卒组可以从当前点的左边或者上边走到该点,假设从原点走到该点左边点的路径条数是 x,从原点走到该点上边点的路径条数是 y,那么从原点走到该点的路径条数就是 x+y
我们设 f ( i , j ) f(i,j) f(i,j) 为从原点走到当前点所需要的路径条数,那么我们可以得到:
f ( i , j ) = f ( i − 1 , j ) + f ( i , j − 1 ) f(i,j)=f(i−1,j)+f(i,j−1) f(i,j)=f(i1,j)+f(i,j1)
所以这道题的最终答案是 f ( n , m ) f(n,m) f(n,m)
接下来我们再将马放入进去,当我们遇到马的时候路径是无效的,所以我们只需要判断当前点为马可移动到的点时让 f ( x , y ) = 0 f(x,y) = 0 f(x,y)=0 即可。

#include "bits/stdc++.h"
using namespace std;

#define ll long long
#define maxn

inline int read() {     //快读
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

const int mx[] = {-2, -1, 1, 2, 2, 1, -1, -2}; //表示马横向的移动范围
const int my[] = {1, 2, 2, 1, -1, -2, -2, -1}; //表示马纵向的移动范围

int n, m, a, b;
ll f[25][25];     //十年OI一场空,不开long long见祖宗
bool c[25][25];   //记录该点是否可以通过

int main(){
    //freopen("","r",stdin);
    //freopen("","w",stdout);
    n = read()+2; m = read()+2; a = read()+2; b = read()+2;
    memset(c,0,sizeof(c));
    memset(f,0,sizeof(f));
    f[2][1] = 1;
    c[a][b] = 1;  //初始化
    for (int i = 0; i < 8; ++i)
        c[a + mx[i]][b + my[i]] = 1; //马所控制的点均不可通过
    for(int i = 2; i <= m; ++i) {
        for(int j = 2; j <= n; ++j){
            if(c[j][i]) continue;
            f[j][i] = f[j - 1][i] + f[j][i - 1];  //状态转移
        }
    }
    printf("%lld", f[n][m]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值