动态规划初见

最近上课讲了动态规划,写这篇当作自己的笔记罢了,希望能温故而知新。

动态规划

动态规划(Dynamic Programming)与分治方法类似,将问题划分成为若干个子问题,求解完子问题后把解组合起来成为原问题的解。但与分治不同的是,动态规划应用于子问题重叠的情况,即不用的子问题有公共的最优子结构(划重点!!!),这样的话我们在求解过程中解决公共子问题时只需求解一次,减少了不必要的计算工作。(我的理解就是把子问题从小到大打个表直接调用就完事了QAQ)

动态规划应用

动态规划用于求解最优化问题,这类问题有很多可行解,我们要在其中找到最优值,我们称我们找到的值为一个最优解。

应用动态规划算法四个步骤(如果不行当然就是用不了DP):

  1. 刻画一个最优解的结构特征。(意味着这道题就是求最优解的,我们能在其中找到每个最优子结构然后可以组合起来求解原问题的最优解,如果不行一定一定不是DP再次强调!!!)
  2. 递归地定义最优解的值(我们已经发现这道题可以使用DP啦,那么肯定首先要通过现有的关系找到最最重要的状态转移方程,有了状态转移方程才能一步一步的从最优子结构打表一直打到原问题)
  3. 计算最优解的值,通常采用从低往上的方法。(求解问题的过程,从头开始循环,注意边界和判断条件)
  4. 利用计算出的信息构造出一个最优解。(这条的意思是我们要拿出来一个方案,什么方案,那就是最优解的方案,我们的最优解到底采用的是什么方式得到,如果只是计算出最优解那么这条完全可以忽略,要实现记录方案也不难,定义信息在步骤三从头计算的时候不断更新维护数值就好了)

流水线调度问题

记录分析一下老师讲过的几个问题,首先是流水线调度问题:汽车制造有两条流水线,求汽车制造的流水线调度的最快方法。

 如图所示就是其可能的流水线调度过程,汽车在装配时可以随意选择两个站点中的一个进入,最优解即为哪条线路消耗时间最短即最优。

我们显然不可能使用穷举法,因为计算的数量是2^n,所以这个时候使用DP就是很好的选择了。我们这时候就要按照之前的四个步骤来分析这个问题,首先判断是否是一个最优解的结构,他的子问题显然是通过Sij最快的路径是什么,如果j = 1那么显然只有一种方式,如果j >= 2那么在每一步都有两个选择,判断哪个消耗时间更短即可,所以问题包含其子问题的最优解。(最佳子结构是动态规划法重要的特点之一,使用最佳子结构可以从子问题的最优解来构造原问题的最优解)所以问题转化成为每一局部更优的选择,从而得到状态转移方程:

 

 通过状态方程进行计算打表就完成了我们的前三个步骤,而要实现第四步只需在计算中不断更新走过的站点即可。

01背包问题

有n个石头,和一个容量为k的背包,对于每个石头都有其重量wi和价值vi,如何装取石头能使背包里装的石头价值最高。

这道题明显是求取最优解的问题,首先我们要想明白对什么参数进行打表,很明显是背包的重量,因为背包的重量一定,我们就能把问题转化成相应子问题最优解的求解。再说说为什么贪心不行,因为可能由于贪心虽然每一件物品性价比都高,但是剩余的空间变少导致整体的价值变少了所以不可以用贪心。

下一步要得到状态方程:

 状态方程含义i代表每一个物品,不断更新j的值确保每一个j代表的重量对应的f[j]都是最优解,最后输出f[k]的值即为答案。

#include <stdio.h>

int v[1005], w[1005];
long long f[100001] = {0};
long long dp[1005][100001];

long long max(long long a,long long b)
{
    if (a>b) return a;
    else return b;
}

int main(){
	int n,k;
	scanf("%d%d",&n,&k);
	
	for(int i = 1;i <= n;i++){
		scanf("%d%d",&v[i],&w[i]);
	}
	for(int i = 1;i <= n;i++){
		for(int j = k;j >= 0;j--){
			if(j - w[i] >= 0)f[j] = max(f[j - w[i]] + v[i],f[j]);
			
		}
	}
	printf("%lld",f[k]);
}

完全背包问题

跟前一问题不同,本次的每件物品可以无限使用,再去求取最大可以获得的价值,输入可以获得的体积V,第二行输入V组数据代表每件物品的价值。

(不大想分析了好累

01背包每件物品状态要么是0要么是1,而完全背包可以无限取,还是列出方程求解,每次都检验是不变好还是把东西去掉,换成一个新的更好,从而打出dp[i]的表得出答案。

#include <stdio.h>
long long max(long long a,long long b)
{
    if (a>b) return a;
    else return b;
}

int main(){
	int v;
	scanf("%d",&v);
	int a[v + 1];
	for(int i = 1;i <= v;i++){
		scanf("%d",&a[i]); 
	}
	long long dp[v + 1];
	for(int i = 1;i <= v;i++){
		long long q = a[i];
		dp[1] = a[1];
		for(int j = i - 1;j >= 1;j--){
			q = max(q,a[j] + dp[i - j]);
		}
		dp[i] = q;
		
	}
	printf("%lld",dp[v]);
}

其他的问题下回再说吧好累啊写这么多……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值