浅尝动态规划

本文介绍了动态规划的概念,包括最优化原理、无后效性和子问题重叠的特性。通过实例展示了动态规划在解决最优化问题时的步骤,如求解矩阵中的最大路径长度。同时,给出了两道动态规划题目——采药问题和疯狂采药问题的解题思路与AC代码,探讨了状态转移方程的重要性。作者表示尽管对动态规划理解仍不深入,但认识到多做练习的重要性。
摘要由CSDN通过智能技术生成

前言

我对动态规划的理解:贪心+递推(存储递推结果),即动态更新每一阶段得出的最优解,解决最优化问题。每一个阶段的决策都会使问题的规模和状态发生变化,整个过程满足最优子结构性质。

三个性质:最优化原理,无后效性,子问题重叠。

动态规划的基本思想:把求解的问题分成多个子问题,按顺序求解各子问题,前一子问题的解为后一子问题的求解提供了信息,在求解任一个子问题的时候,列出所有可能的局部解,通过决策(判断)保留那些有可能达到局部最优的局部解,舍弃其他局部解,直到求解到最后一个子问题的解也就是初始问题的解。

由于动态规划有子问题重叠的特点,为了减少重复计算,对每一个子问题只解一次,将其他不同阶段的不同状态保存在一个二维数组中。

解题基本步骤:

  1. 划分阶段
  2. 选择状态
  3. 确定决策并写出状态转移方程(划重点)

接触动态规划的第一道题:ZUEBOJ icon-default.png?t=LA92http://oj.icode8.cn/problem.php?cid=1134&pid=20

在看完动态规划后做这个题有了思路,下面是代码(可能会写的有些麻烦)

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
	int n,m,max,t;
	int z[103][103],d[103][103];
	while(cin >> n >> m){
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				cin >> z[i][j];
				if(i==0||j==0){//边界最大是1 
					d[i][j]=z[i][j];
				}
			}
		}
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				if(z[i][j]==1){//如果该位置符合条件 
					if(i==0){
						d[i][j]=1;
					}
					else if(j==0){
						d[i][j]=1;
					}
					else{//d[i][j]等于左边,左上角,上边的最小值加一 
						t=min(d[i-1][j],d[i-1][j-1]);
						d[i][j]=min(t,d[i][j-1])+1;
					}					
				}
				else{
					d[i][j]=0;
				}
			}
		}
		max=0;
		//找到最大值输出 
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				if(max<d[i][j]){
					max=d[i][j];
				}
//				cout << d[i][j] << " ";
			}
//			cout << endl;
		}
		cout << max << endl;
	}
    return 0;
}

接下来是接触动态规划的两道题,让我百思不得其解,最终看洛谷题解写出

链接:采药疯狂采药

AC代码

采药:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
	ll t,m;
	int z[10005],h[10005],dp[105][1005];
	//z[i]代表采取第i株药的时间
	//h[i]代表第i株草药的价值
	//dp[i][j]代表以j为容量为放入前i个物品(按i从小到大的顺序)的最大价值 
	while(cin >> t >> m){
		for(int i=1;i<=m;i++){
			cin >> z[i] >> h[i];
		}
		for(int i=1;i<=m;i++){
			for(int j=t;j>=0;j--){
				if(j>=z[i]){
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-z[i]]+h[i]);
					//所谓的状态转移方程 
				}
				else{
					dp[i][j]=dp[i-1][j];
				}
			}
		}
		cout << dp[m][t] << endl;
	}
    return 0;
}

关于采药有一个不理解的地方,这样就是错的

	for(int i=1;i<=m;i++){
			for(int j=t;j>=z[i];j--){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-z[i]]+h[i]);
			}
		}

crazy采药(枚举草药,尝试可放入草药的背包 ):

#include<iostream>
#include<algorithm>
//#define int long long
#include<stdio.h>
using namespace std;
typedef long long ll;
const int p=1e4+5,q=1e7+5;
ll z[p],h[p],dp[q];
int main(){
	ll t,m;
	//z[i]代表采取第i株药的时间
	//h[i]代表第i株草药的价值
	//dp[t]表示采药时间t能获得的最大价值 
	while(cin >> t >> m){
		//t=v  m=n
//	while(scanf("%lld%lld",&t,&m)!=EOF){
		for(int i=1;i<=m;i++){
//			scanf("%lld%lld",&z[i],&h[i]);
			cin >> z[i] >> h[i];
		}
		for(int i=1;i<=m;i++){
			for(int j=z[i];j<=t;j++){
				dp[j]=max(dp[j],dp[j-z[i]]+h[i]);
				/*
				最重要的****
				状态转移方程 
				*/
			}
		}
//		printf("%lld\n",dp[t]);
		cout << dp[t] << endl;
	}
    return 0;
}

最后,我对动态规划还是超级灰常极其不理解,以后还是要多做多练,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值