动态规划的那些事儿

动态规划是一个重要的算法,这几天我搜集了一些相关的题目,这些题目在leetcode上都可以找到


House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

大意是说在一个数组中选择一系列元素使得其和最大。这些元素不能相连。比如说,选择了a[0]就不能选择a[1]。

这是一道比较典型的动态规划题目,因为我们可以把这个问题划分为0.1。。。。n个阶段,并且每一个阶段都只和前面的阶段有关系。
我的想法是用d[i]来表示第i个元素之前(包括第i个元素)的最大和。那么,其推导式是d[i]=max{d[i-2]+a[i-1],d[i-1]}。(a[i-1]是指第i项)这里得绕一下弯子思考一下,如果d[i-1]包含了第i-1项(a[i-2]),那么这个是直接成立的。如果没有包含,那么d[i]必然是d[i-2]+a[i-1]。

代码如下

  int rob(vector<int>& nums) {
        if(nums.empty())return 0;
        vector<int> d(1+nums.size(),0);
        d[0]=0;
        d[1]=nums[0];
        for(int i=2;i<=nums.size();i++)
          d[i]=max(d[i-1],d[i-2]+nums[i-1]);
        return d[nums.size()];
    }

这个算法在空间上还可以优化,因为i阶段的最大值只和i-2和i-1阶段有关系,所以只保留这两个值就可以。

 int rob(vector<int>& nums) {
      int prepre=0;
      int pre=0;
      int cur=0;
      for(int i=0;i<nums.size();i++){
            cur=max(pre,prepre+nums[i]);
            prepre=pre;
            pre=cur;
      }
      return cur;
    }

Climbing Stairs

You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

爬一个n阶的楼梯,每一次走1步或者2布,有多少中走法。这个题目也很明显有阶段性,可以想象一个d[i]数组来表示第i阶段的走法。

d[i]=d[i-2]+d[i-1];

int climbStairs(int n) {
        vector<int> d(n+1,0);
        d[0]=1;//注意,到达
        d[1]=1;
        for(int i=2;i<=n;i++){
            d[i]=d[i-1]+d[i-2];
        }
        return d[n];
    }

很容易发现,d[i]这个数组也可以用两个变量来代替。

int climbStairs(int n) {
        vector<int> d(n+1,0);
        d[0]=1;
        d[1]=1;
        for(int i=2;i<=n;i++){
            d[i]=d[i-1]+d[i-2];
        }
        return d[n];
    }

Best Time to Buy and Sell Stock

Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

这道题乍看上去觉得难道不应该是求数列中最大值和最小值的和么?
但是其实仔细思考一下,其实是求a[i]-a[j] (i>j)的最大值

我们先贴上一段代码

int maxProfit(vector<int>& prices) {
        if(prices.size()==0) return 0;
        int profit=0;
        int min=prices[0];
        for(int i=0;i<prices.size();i++){
            if(prices[i]-min>profit)
                profit=prices[i]-min;
            else if(prices[i]<min)
                min=prices[i];
        }
        return profit;
    }

这段代码是正确的。现在我们用动态规划的思路来做一下。
首先,profit[i]代表的是第i天之前可以取得的最大差,那么,其和profit[i-1]之间的关系就是,profit[i]=max{profit[i-1],price[i]-min[i-1]}。
但是别忘记了更新min。min[i]=min{min[i-1],price[i]}。

我们发现,因为只和之前第i-1阶段的profit[i-1]和min[i-1]有关系(price数组已经给出),所以这里只需要两个变量就可以了,即profit和min。

这个题目还告诉我们,用来表示当前阶段的状态的方式能简则简,可以让代码更清晰。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值