2944.购买水果需要的最少金币数

法一: 动态规划 O(n^2)

买了下标 i 的水果, 可以免费获得 (i+1) 个水果

dp[i][0] 表示 获得 0...i 的水果需要的 最少金币(i是免费获得的) 

dp[i][1] 表示 获得 0...i 的水果需要的 最少金币(i是购买获得的) 

dp[i][1] = min(dp[i-1][0],dp[i-1][1]) + prices[i]

dp[i][0] = min(dp[j][1])    j=i/2...i-1          // dp[i][0] :  i要是想免费获得,必须是之前买了一个水果

class Solution {
public:
    int minimumCoins(vector<int>& prices) {
        int n=prices.size();
        int dp[n][2]; 
        dp[0][0]=INT_MAX/2; dp[0][1]=prices[0];
        for(int i=1;i<n;i++){
            dp[i][0]=dp[i][1]=INT_MAX;
            for(int j=i/2;j<i;j++){
                dp[i][0]=min(dp[i][0],dp[j][1]);
            }//i=2t j=t     i=2t+1 j=t
            dp[i][1]=min(dp[i-1][0],dp[i-1][1])+prices[i];
        }
        return min(dp[n-1][0],dp[n-1][1]); //针对只有一个的  不然最小的铁定是 dp[n-1][0]
    }
};

法二: 动态规划 O(n^2)

我们用一个dp[i]表示 当前买完 i...len-1 的水果需要的总金币

dp[i] = prices[i] + min dp[j]   i+1 <= j <= 2*i+2                                                      i < (len-1)/2 

dp[i] = prices[i]                                                                                                     i >= (len-1)/2 

//记忆化搜索版本
class Solution {
public:
    int minimumCoins(vector<int>& prices) {
        //定义dfs[i]表示购买第i个,以及后面所有的水果需要的总花费
        //买了第i个,那么可以免费获得第i+1,i+2...2i个水果
        //下一个需要购买的就是 i+1,i+2,...2i+1,取这些情况下的最小值
        int len=prices.size();
        int memo[len]; memset(memo,-1,sizeof(memo));
        function<int(int)> dfs=[&](int i)->int{
            if(i>=(prices.size()+1)/2) return prices[i-1];
            int &res=memo[i];
            if(res!=-1) return res;
            int price=INT_MAX;
            for(int j=i+1;j<=2*i+1;j++){
                price=min(price,dfs(j));
            }
            return res=price+prices[i-1];
        };
        return dfs(1);
    }
};
//dp版本
class Solution {
public:
    int minimumCoins(vector<int>& prices) {
        //转换成dp,这里我们是把 dp[0...len-1] 对应 prices[0...len-1]
        //买了下标i 可以免费获得i+1...i+i+1(一共i+1个) 
        //dp[i] 可以从 dp[i+1]...dp[i+i+1],dp[i+i+2]这些转移过来
        int len=prices.size();
        int dp[len];
        for(int i=len-1;i>=0;i--){
            if(i>=(len-1)/2){
                dp[i]=prices[i]; continue;
            }
            dp[i]=INT_MAX/2;
            for(int j=i+1;j<len && j<=2*i+2;j++) 
                dp[i]=min(dp[i],dp[j]);
            dp[i]+=prices[i];
        }
        return dp[0];
    }
};

法三: 单调队列 O(n)

        买了下标i可以免费获得i+1...i+i+1(一共i+1个),也就是说dp[i]可以从 dp[i+1]...dp[i+i+1],dp[i+i+2]这些转移过来 也就是min{dp[j]} j=i+1...2i+2

        注意到随着 i 的变小, j 的左右边界也在变小,所以可以使用类似滑动窗口的方法求最值,我们使用单调队列, 单调栈或者单调队列一般是按照for的遍历顺序拿到当前元素的,也就是说比如对于单调队列来说

        我们的队头到队尾的序列是0,1,2...len-1的子序列或者是len-1,len-2,...0的子序列,明白了这一点,对于我们选择pop或者push的位置很重要

class Solution {
public:
    int minimumCoins(vector<int>& prices) { 
        deque<int> dq;
        int len=prices.size();
        int dp[len];
        for(int i=len-1;i>=0;i--){
            while(dq.size() && dq.front()>2*i+2){//移除那些不满足限制的元素  如果front等于 2i+2
                dq.pop_front();
            }
            if(i>=(len-1)/2){
                dp[i]=prices[i];
            }else{
                dp[i]=prices[i]+dp[dq.front()];
            }
            while(dq.size() && dp[i]<=dp[dq.back()]){//维护当前的最小值 注意这里是要找当前i对应的窗口的最小值dp[j] 所以要用dp而不是prices
                dq.pop_back();
            }
            dq.push_back(i);//这里添加到后面就是队头下标大 队尾下标小
        }
        return dp[0];
    }
};

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
疫情居家办公系统管理系统按照操作主体分为管理员和用户。管理员的功能包括办公设备管理、部门信息管理、字典管理、公告信息管理、请假信息管理、签到信息管理、留言管理、外出报备管理、薪资管理、用户管理、公司资料管理、管理员管理。用户的功能等。该系统采用了MySQL数据库,Java语言,Spring Boot框架等技术进行编程实现。 疫情居家办公系统管理系统可以提高疫情居家办公系统信息管理问题的解决效率,优化疫情居家办公系统信息处理流程,保证疫情居家办公系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理疫情居家办公系统信息,包括外出报备管理,培训管理,签到管理,薪资管理等,可以管理公告。 外出报备管理界面,管理员在外出报备管理界面中可以对界面中显示,可以对外出报备信息的外出报备状态进行查看,可以添加新的外出报备信息等。签到管理界面,管理员在签到管理界面中查看签到种类信息,签到描述信息,新增签到信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值