RE:从零开始的算法之路第五章

本文介绍了动态规划(Dynamic Programming, DP)的基本概念和应用,强调了DP与分治法的区别,并详细讲解了基础DP的四种经典问题:硬币问题、0/1背包问题、最长公共子序列和最长递增子序列。通过实例分析和代码展示,阐述了DP的解题思路和优化技巧,如状态转移和记忆化搜索。此外,还提到了递推与记忆化搜索在解决特定问题时的优势。" 107292685,9282403,Java递归遍历文件夹及txt操作,"['Java', '文件系统操作', '文本处理']
摘要由CSDN通过智能技术生成

0.介绍

动态规划(Dynamic Program,DP)和分治方法核心差不多,将一个复杂的问题分解为相对简单的子问题,最后得出答案。但是分冶法子问题是相对独立无关的,而动态规划子问题前后相关,而且非常相似处理方法几乎一样。所以可以把前面的子问题的计算结果记录为一种"状态",后面子问题直接查找前面得到的状态避免了重复计算
DP题有三步:定义状态,状态转移,算法实现
DP可以分成线性和非线性的:
1.线性DP:顺推与逆推,常用表格来处理状态
2.非线性DP:例如树形DP,建立在树上。
可以通过根到叶,根传给有用信息给子节点
可以通过叶到根,根的子节点给有用的信息给根

1.基础DP

介绍

基础DP问题直观易于理解,状态容易,表示转移方程容易得到

例题

1.1硬币问题

Coin Change [HDU - 2069]
假设有5种硬币:50美分,25美分,10美分,5美分和1美分。 我们希望以给定的金额使用这些硬币进行更改。

例如,如果我们有11美分,则可以用一枚10美分硬币和一枚1美分硬币,或两枚5美分硬币和一枚1美分硬币,或一枚5美分硬币和六枚1美分硬币进行找零。 分硬币或11个1分硬币。 因此,使用上述硬币,有四种方法可以使11分钱找零。 请注意,我们认为有一种方法可以使零美分发生变化。

编写一个程序,以查找以美分计的任何金额进行更改的不同方式的总数。 您的程序应该能够处理多达100个硬币。
输入
输入文件包含任意数量的行,每行包含一个数字(≤250),以分币为单位。
输出
对于每条输入线,输出一条线,其中包含使用上述5种硬币进行更改的不同方式的数量。
样本输入
11
26
样本输出
4
13
思路

  • 思路递归从后往前,但有很多重复部分
  • 所以从前往后打表
  • 若没有100总硬币的需求,那只要2个循环打表DP[j] = DP[j] + DP[j - type[i]];(type是钱币种类)就行
  • 将DP设为二维数组,横坐标i为钱的总数纵坐标j为钱币量的数,若i能用j个钱币表示则DP[i][j]=1;
  • 数组大小开为DP[251][101];钱的限制*币数限制
    代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
const int maxn = 1e5 + 10;
#define re(x) for(int i=0;i<x;++i)
#define rey(x) for(int j=0;j<x;++j)
#define Cheak(x,y) (x<W&&x>=0&&y>=0&&y<H)
typedef long long LL;
using namespace std;
int dir[4][2] = {
    {
   -1,0},{
   0,-1},{
   1,0},{
   0,1} };
int T, n, a[maxn], m, * p, flag = 1;
int type[5] = {
    1,5,10,25,50 }, DP[251][101] = {
    0 };
int main()
{
   
	ios::sync_with_stdio(0); cin.tie(0);
	DP[0][0] = 1;
	re(5) {
   
		for (int j = 1; j < 101; j++)
		{
   
			for (int k = type[i]; k < 251; k++)
				DP[k][j] += DP[k - type[i]][j - 1];
		}
		
			
			
	}
	int ans[251] = {
    0 };
	re(251)
		rey(101)
		ans[i] += DP[i][j];
	while (cin>>n)
		cout << ans[n]<<endl;
	


}

1.2 0/1背包

有一个人被称为“骨收集者带着V的体积的背包收集骨头的过程中,有很多骨头,显然,不同的骨头具有不同的价值和不同的体积,现在给定骨头在行进过程中的价值,您能算出吗?骨收集器能获得的总价值的最大值是多少?

输入
第一行包含一个整数T,即个案数。
紧随其后的是T个案例,每个案例三行,第一行包含两个整数N,V(N <= 1000,V <= 1000),它们表示骨头的数量和包的体积。第二行包含代表每个骨骼值的N个整数。第三行包含N个整数,代表每个骨骼的体积。
输出
每行一个整数,代表总和的最大值(该数字将小于231)。
样本输入
1
5 10
1 2 3 4 5
5 4 3 2 1
样本输出
14
思路

  • 设一个二维数组dp[i][j],j代表所用空间,i代表物品序号
  • 只装第一个时,i=1,j从第一个重量开始,无论占多少重量,都是一个价值
  • 然后装第二个,如果比上一行的减它重量的列加它价值还高的话,该列刷新成新的价值,相当于第一第二都放进去了
  • 因为下一行只于上一行有关,所以不需要二维数组(当不需要知道放的是什么时)
    代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
const int maxn = 1e5 + 10;
#define re(x) for(int i=0;i<x;++i)
#define rey(x) for(int j=0;j<x;++j)
#define Cheak(x,y) (x<W&&x>=0&&y>=0&&y<H)
typedef long long LL;
using namespace std;
int dir[4][2] = {
    {
   -1,0},{
   0,-1},{
   1,0},{
   0,1} };
int T, n, a[maxn], m, * p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值