算法初探系列13——线性DP初步

概述

上节课我们学习了递推(新手建议看),这节课我们来学习和递推很类似动态规划,即DP


基本思想

  • 问题的答案可以通过子问题的答案推出。
  • 让我们先回顾一下递归的基本思想:例如问题的参数为(10,11),我们可以将他化解为几个子问题,这几个子问题再继续化解,直到所有子问题都是有解的,在自底向上累加。
  • 动态规划其实就是记忆化搜索(不会的小伙伴可以忽略),每次将子问题的解存到一个表里,以后需要直接调用就可以,不用每次都继续向下分。
  • 目前,我们可以暂时认为动态规划就是递推

术语

  • 状态
    子问题。
  • 决策
    在目前状态下的所有选择,比如在迷宫里往那个方向走。
  • 状态转移方程
    关于两个状态的等式,即如何由一个状态的解求出另一个状态的解。
  • 策略
    即可行方案。
  • 最优子结构
    满足全局最优化的话必须所有局部最优化。
  • 无后效性
    后面状态的解不受目前状态的影响。

使用条件

  • 最优子结构
  • 无后效性

实现步骤

  • 状态定义
  • 求出状态转移方程
  • 找到边界条件

题目1:蒟蒻君逛超市

题目

超市举行活动,从(1, 1)走到(n, m),只能往下或者往右走。蒟蒻君每到达一个格子,就可以拿到一定的money。请问,蒟蒻君最多能拿到多少块钱?例如:
在这里插入图片描述
可以这么走:
在这里插入图片描述

分析

设aij为(i, j)的拿钱数。

  • 状态定义
    设dpij为从(1, 1)走到(i, j)的最多拿钱数。
  • 状态转移方程
    每个格子(除了第一排和第一列的)可以从左边或者上边过来,因此:
    dpij = max(dpi-1j, dpij-1) + aij
  • 边界条件
    (1, 1)是初始位置,不需要走。
    第一排的只能从左边过来,此时:
    dpij = dpij-1 + aij
    第一列的只能从上边过来,此时:
    dpij = dpi-1j + aij

实现

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int a[N][N], dp[N][N];
int main() {
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			cin >> a[i][j];
			if (i == 1 && j == 1) {
				dp[i][j] = a[i][j];
			} else if (i == 1) {
				dp[i][j] = dp[i][j - 1] + a[i][j]; 
			} else if (j == 1) {
				dp[i][j] = dp[i - 1][j] + a[i][j];
			} else {
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
			}
		}
	}
	cout << dp[n][m] << '\n';
	return 0;
}


题目2:蒟蒻君干饭

题目

蒟蒻君去爬山啦~爬到山顶后,很饿。。
这座山共有n层,第i层有i个饭店。蒟蒻君很自律,每层只去一个饭店。
在第i层第j个饭店可以吃aij斤食物(蒟蒻君都吃了),到达第(i + 1)层(第n层就到底了,不能再去饭店)的第i个和第(i + 1)个饭店。
例如:
在这里插入图片描述
在这座山中,可以这样走:
在这里插入图片描述

分析

  • 状态定义
    设dpij为走到第i层第j个饭店能吃的最大斤数。
  • 状态转移方程
  • 所有饭店都可以从正上方或者左上方的饭店走过来(下标从1开始,不用考虑越界),即:
    dpij = max(dpi-1j, dpi-1j-1) + aij
  • 边界条件*
    不需要考虑。

实现

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int a[N][N], dp[N][N];
int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= i; ++j) {
			cin >> a[i][j];
			dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + a[i][j]; 
		}
	}
	// 答案 = 最下层所有位置的最大值中的最大值
	int res = ~0x3f3f3f3f;
	for (int i = 1; i <= n; ++i) {
		res = max(res, dp[n][i]);
	} 
	cout << res << '\n';
	return 0;
}

题目3:[NOIP2002普及组]过河卒

传送

这节课我们学习了最简单的线性DP,下节课我们来学习线性DP的经典问题。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻一枚

谢谢鸭~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值