LeetCode - 星期总结01

题目 309. 最佳买卖股票时机含冷冻期

难度 中等

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:

输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

动态规划的问题

对于动态规划这个词,我们已经见过很过次了。

确定状态
从这道题来说,分析出每一天的状态是最最重要的点。
0.不持股且当天没卖出,定义其最大收益dp[i][0];
1.持股,定义其最大收益dp[i][1];
2.不持股且当天卖出了,定义其最大收益dp[i][2];

初始化状态值

dp[0][0]=0;
dp[0][1]=-1*prices[0];
dp[0][2]=0;

更新状态值
在确定了一开始的几种可能状态后,我们后边要做的事情,就是不断更新这些状态值。

dp[i][0]=Math.max(dp[i-1][0],dp[i-1][2]);
dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
dp[i][2]=dp[i-1][1]+prices[i];

总结
以后遇到动态规划的问题,一定要先分析好状态。尤其是一眼看过去遍历会超时,而通过保存某一状态的值能得到下一次运算的结果的时候,大概率就要使用动态规划。而确定状态这一步是最重要的。

题目 315. 计算右侧小于当前元素的个数

难度 困难

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

归并排序 + 索引数组

归并排序求逆序数

这道题里面,我重新温习了一遍归并排序的算法,以及了解到了索引数组的作用。
并且,了解了何为逆序数,如何通过归并排序求解逆序数。

设有数列{6,202,100,301,38,8,1}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1};

第二次归并后:{6,100,202,301},{1,8,38};

第三次归并后:{1,6,8,38,100,202,301};

最终结果得出{1,6,8,38,100,202,301}

归并排序是一种稳定且高效的排序算法,时间复杂度为O(nlog2n) ,一般按照三步走:

1.划分问题:把序列分成元素个数尽量相等的两半

2.递归求解:把两半元素分别排序

3.合并问题:把两个有序表合并为一个

前两步很容易实现,关键在于第三步,怎么合并呢? 很简单,每次只需把左右两边序列的最小元素进行比较,把其中较小的元素加入到合并后的辅助数组中即可。

实例代码

//归并排序
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
void merge_sort(int l , int r)
{
    if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
    {
        int mid = (l+r) / 2 ;
        int i = l; //辅助数组的下标
        int p = l , q = mid+1;
        merge_sort(l , mid);
        merge_sort(mid+1 , r);
        //printf("%d-%d  %d-%d\n",p,mid ,q ,r);
        while(p<=mid || q<=r)//左右两部分只要有一部分不为空
        {
            if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
                b[i++] = a[p++];
            else b[i++] = a[q++];//从右半数组复制到辅助数组
        }
        for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
            a[i] = b[i];
    }
}
int main()
{
    int n;
    while(cin >> n)
    {
        for(int i = 1 ; i <= n; i ++)
            cin >> a[i];
        merge_sort(1 , n);
        for(int i = 1; i <= n; i++)
            cout << a[i] << " ";
        cout << endl;
    }
    return 0;
}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;

第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;

第三次归并后:{1,6,8,38,100,202,301},比较次数:4;

总的比较次数为:3+4+4=11;

逆序数为14;逆序对数为 7 对

根据归并排序的特性(左右两部分的有序序列合并时,假设i在左边,j在右边,对于右边的j,统计左边比它大的元素个数f(j),则f(j) = mid-i+1 ,合并万所有的序列时即可得出答案,即f(j)之和便是答案),只需将上面的代码修改一处:把“else b[i++] = a[q++];”改成“ else {b[i++] = a[q++]; cnt += mid-p+1;}" ,注意在调用之前将cnt清零。

以上就是归并排序求解逆序数的方法。

索引数组

这张图应该能解释清楚了
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值