leetcode(C++)买卖股票的最佳时机II和动态规划钢条切割实例

继续数组标签里的题目。

1.买卖股票的最佳时机II

注意必须在再次购买前出售掉之前的股票,当天卖了还能买。这道题我觉得还挺难的,而且这个暴力法(回溯法)我自己写不出来。下面的代码是官方题解方法一暴力法的C++版本,这个提交的话无法通过,有测试用例超时了,但方法是没有问题的。

1)暴力法

有递归的思想在里面,如果选择在第i天卖掉股票,其后的天数的买卖股票成为一个可以独立求解的子问题。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        return calculate(prices,0);

    }
public:
    int calculate(vector<int>& prices,int s){
        if(s>=prices.size()) return 0;
        int max=0;
        for(int start=s;start<prices.size();start++){
            int maxprofit=0;
            for(int i=start+1;i<prices.size();i++){
                int profit=calculate(prices,i+1)+prices[i]-prices[start];
                if(profit>maxprofit)
                    maxprofit=profit;
            }
            if(maxprofit>max)
                max=maxprofit;
        }
        return max;
    }
};

2)贪心,参考的精选题解。

贪心算法总是做出局部最优的选择,寄希望这样的选择能导致全局最优解。但是我感觉这道题目你只要明白了当天卖了还能买,你不错过任何一个增值的机会,只要第二天比这一天的价格高就选择买然后第二天卖,这样累计下来就是最大利润,这样写代码也异常简单。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
if(prices.size()==0||prices.size()==1) return 0;
    int maxprofit=0;
    for(int i=0;i<prices.size()-1;i++){
        if(prices[i]<prices[i+1]){
            maxprofit=maxprofit+prices[i+1]-prices[i];
        }
    }
    return maxprofit;
    }
};

3)动态规划

回想一下买卖股票的最佳时机的那道题目,在每个卖出时机都算一下这一天卖出的话和历史最低价格的差(这里说的历史最低价格是:当前在第i天的话,第1,2,...,i-1天中的最低价格),这样一定不会错过最大利润。

这道题目的话,需要想清楚状态,和状态转移方程。状态有两种:持有股票1和持有现金0。注意不限制交易次数

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    int len=prices.size();
    if(len<=1) return 0;
    int dp[len][2];
    dp[0][0]=0;dp[0][1]=-prices[0];
    for(int i=1;i<len;i++){
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);//持有现金状态由两种状态转移而来:上一天持有现金然后今天什么都不做或者上一天是持有股票状态今天抛出
        dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
    }
    return dp[len-1][0];
    }
};

动态规划笔记:

为了求解规模为n的原问题,我们先求解形式完全一样但规模更小的子问题。什么问题适合用动态规划来求解呢?最优子结构性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。不同于朴素递归,动态规划方法仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。

动态规划有两种等价的实现方法。1.带备忘的自顶向下法,这种方法记录子问题的解以备随后查找   2.自底而上法。这里记录一下算法导论中钢条切割问题的朴素递归实现方法和这两种实现方法,以及不仅返回最优收益值还返回切割方案的版本。

int cutrod(int p[],int n){//朴素递归
    if(n==0) return 0;
    int q=INT_MIN;
    for(int i=0;i<n;i++){
        q=max(q,p[i]+cutrod(p,n-1-i));
        //cout<<q<<endl;
    }
    return q;

}
class Solution{//带备忘的自顶而下法
public:
    int memocutrod(int p[],int n){
        int r[n+1];
        for(int i=0;i<n+1;i++)
            r[i]=INT_MIN;
        return memocutrod_aux(p,n,r);
    }
    int memocutrod_aux(int p[],int n,int r[]){
        if(r[n]>=0) return r[n];
        int q;
        if(n==0) q=0;
        else
        {
            q=INT_MIN;
        for(int i=0;i<n;i++)
            q=max(q,p[i]+memocutrod_aux(p,n-1-i,r));
        }
        r[n]=q;
        return q;
    }
};
int bottomupcutrod(int p[],int n){//自底向上法
    int r[n+1];int q;
    r[0]=0;
    for(int j=1;j<n+1;j++){
        q=INT_MIN;
        for(int i=0;i<j;i++)
            q=max(q,p[i]+r[j-1-i]);
        r[j]=q;
    }
    return r[n];
}
struct result{
    int r[20];
    int s[20];
};
result bottomupcutrod2(int p[],int n){//记录最优解的构成
    struct result results;
    int q;
    results.r[0]=0;
    results.s[0]=0;
    for(int j=1;j<n+1;j++){
        q=INT_MIN;
        for(int i=0;i<j;i++){
            if(q<p[i]+results.r[j-1-i]){
                q=p[i]+results.r[j-1-i];
                results.s[j]=i+1;
            }
        }
        results.r[j]=q;
    }
    return results;
}
void print(int nums[]){
    int *p=nullptr;
    for(p=nums;p<nums+11;p++)
        cout<<*p<<" ";
    cout<<endl;
}
int main()
{
    //int a;
    //Solution s;
    //vector<int> prices={7,1,5,3,6,4};
    //a=maxProfit(prices);
    int p[10]={1,5,8,9,10,17,17,20,24,30};
    //cout<<bottomupcutrod(p,7)<<endl;
    struct result results;
    results=bottomupcutrod2(p,10);
    print(results.r);
    print(results.s);
    cout << "Hello world!" << endl;
    return 0;
}

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值