动态规划(HDOJ2084,凑零钱问题)

基本思想

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。**我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。**这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

动态规划一般分为四步
1、找出处理问题的最后一步,把问题化成子问题。
2、根据第一步写出状态转移方程
3、找出初始条件和边界情况
4、确定计算顺序(从初始条件开始)

下面用两个例子演示一下
HDOJ2084数塔问题:题目描述
按照上述步骤
1、无论第n-1层选择哪一个结点,下一层必定要选择与该点相邻的最大点。所以最好一步是找出与n-1结点相邻的最大点。
2、设前n-1部最大路线经过的第n-1个结点为num[i][j],则该条路线上最后两数之和为dp[i][j]=num[i][j]+max{num[i+1][j],num[i+1][j+1]},即为状态转移方程
3、本题初始条件即是最后一层数据
4、因为初始条件在最后一层,所以从下往上计算。如果从上往下计算就是贪心法,不能得到正确结果。

#include <iostream>
#include<cstdio>
using namespace std;

int max(int a, int b)
{
 if (a > b)
  return a;
 else
  return b;
}
int main()
{ 
 int i, j, k,n,t,num[101][101];
 while (cin >> t)
 {
  while (t)
  {
   cin >> n;
   for (i = 0; i < n; i++)
    for (j = 0; j <= i; j++)
     cin >> num[i][j];
   for (i = n - 2; i >= 0; i--)
    for (j = 0; j <= i; j++)
     num[i][j] += max(num[i + 1][j], num[i + 1][j + 1]);
   cout << num[0][0] << endl;
   t--;
  }
 }
 return 0;
}

凑零钱问题
如果有2元,5元,7元,三种硬币,要求正好凑成27元,问最少用几枚硬币。

这个问题很容易想到贪心算法,即先用尽量大的硬币,比如三个7元凑出21,剩下6元用其他,比如3个2元,得到答案6枚。但这个结果是错误的。因为使用7元时剩下的硬币数并不一定最小,可能因为不能凑整反而较大。

按照动态规划步骤
1、无论前面怎么选,当我们挑选最后一枚硬币时有,2,5,7这三种选择
2、设f(27)是27元的最少硬币数,则f(27)=min{f(25)+1,f(22)+1,f(20)+1},即为状态转移方程
3、初始条件,f(0)=0;f(负数)=f(1)=无穷;
4、依次求f(i),i从0循环到27

此处大概模拟一下运行过程:
f(0)=0
f(1)=无穷
f(2)=min{f(0)+1,f(-3)+1,f(-5)+1}=1
f(3)=min{f(1)+1,f(-2)+1,f(-4)+1}=无穷
f(4)=min{f(2)+1,f(-1)+1,f(-3)+1}=2

f(27)=min{f(25)+1,f(22)+1,f(20)+1}=5

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值