502.IPO

题目

假设 力扣(LeetCode)即将开始 IPO 。为了以更高的价格将股票卖给风险投资公司,力扣 希望在 IPO 之前开展一些项目以增加其资本。 由于资源有限,它只能在 IPO 之前完成最多 k 个不同的项目。帮助 力扣 设计完成最多 k 个不同项目后得到最大总资本的方式。

给你 n 个项目。对于每个项目 i ,它都有一个纯利润 profits[i] ,和启动该项目需要的最小资本 capital[i]

最初,你的资本为 w 。当你完成一个项目时,你将获得纯利润,且利润将被添加到你的总资本中。

总而言之,从给定项目中选择 最多 k 个不同项目的列表,以 最大化最终资本 ,并输出最终可获得的最多资本。

答案保证在 32 位有符号整数范围内。

示例 1:

输入:k = 2, w = 0, profits = [1,2,3], capital = [0,1,1]
输出:4
解释: 由于你的初始资本为 0,你仅可以从 0 号项目开始。
在完成后,你将获得 1 的利润,你的总资本将变为 1。
此时你可以选择开始 1 号或 2 号项目。
由于你最多可以选择两个项目,所以你需要完成 2 号项目以获得最大的资本。
因此,输出最后最大化的资本,为 0 + 1 + 3 = 4。

示例 2:

输入:k = 3, w = 0, profits = [1,2,3], capital = [0,1,2]
输出:6

提示:

  • 1 <= k <= 105
  • 0 <= w <= 109
  • n == profits.length
  • n == capital.length
  • 1 <= n <= 105
  • 0 <= profits[i] <= 104
  • 0 <= capital[i] <= 109

    思路

  • 银行家算法*
    将capital和profits配对,并按照captial从小到大进行排序。
    每次对比w和capitali,如果w比capitali大,则将对应的profitsi加入到优先队列中。
    全部加完后,从优先队列中选择最大的profits加到w中。
    重复上述k次。

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Solution {
    public:
    int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
    int n = profits.size();
    vector<pair<int,int>> arr(n);
    priority_queue<int,vector<int>,less<int>> pq;
    for(int i=0;i<n;++i){ arr[i] = {capital[i],profits[i]}; }
    int idx = 0;
    sort(arr.begin(),arr.end());
    for(int i=0;i<k;++i){
    while(idx<n&&arr[idx].first<=w){
    pq.push(arr[idx++].second);
    }
    if(!pq.empty()){
    w+=pq.top();
    pq.pop();
    }else{
    break;
    }
    }
    return w;
    }
    }

931.下降路径最小和

题目

给你一个 n x n方形 整数数组 matrix ,请你找出并返回通过 matrix下降路径最小和

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)(row + 1, col) 或者 (row + 1, col + 1)

示例 1:

输入: matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出: 13
解释: 下面是两条和最小的下降路径,用加粗+斜体标注:
[[2,1,3], [[2,1,3],
[6,5,4], [6,5,4],
[7,8,9]] [7,8,9]]

示例 2:

输入: matrix = [[-19,57],[-40,-5]]
输出: -59
解释: 下面是一条和最小的下降路径,用加粗+斜体标注:
[[-19,57],
[-40,-5]]

示例 3:

输入: matrix = [[-48]]
输出: -48

提示:

  • n == matrix.length
  • n == matrix[i].length
  • 1 <= n <= 100
  • -100 <= matrix[i][j] <= 100

    思路

  1. DFS(超时)
    从上往下,遍历所有的情况。每次选择下面三支中路径和最小的加到上当前值作为从当前路径起始的路径长度。
  2. 动态规划
    从下往上,依次遍历每一层每个节点作为起点到最下面的路径长度最小值。
    最后取第一行最小的即可。

    代码

  3. DFS
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Solution {
    public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
    if(matrix.size()<1) return 0;
    int val = INT_MAX;
    for(int i=0;i<matrix[0].size();i++){
    val = min(val,minFallingPathSum(matrix,0,i));
    }
    return val;
    }
    int minFallingPathSum(vector<vector<int>>& m,int y,int x){
    if(y>=m.size()) return 0;
    if(x<0||x>=m[0].size()) return INT_MAX;
    int a = minFallingPathSum(m,y+1,x-1);
    int c = minFallingPathSum(m,y+1,x);
    int b = minFallingPathSum(m,y+1,x+1);
    int g = min(a,min(b,c));
    if(g==INT_MAX) return INT_MAX;
    return g+m[y][x];

    }
    };
  4. 动态规划
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Solution {
    public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
    int m = matrix.size(),n=matrix[0].size();
    vector<vector<int>> dp(m,vector<int>(n,0));
    for(int x=0;x<n;++x) dp[m-1][x] = matrix[m-1][x];
    for(int y=m-2;y>=0;--y){
    for(int x=1;x<n-1;++x){
    int val = dp[y+1][x];
    if(x-1>=0) val = min(val,dp[y+1][x-1]);
    if(x+1<n) val = min(val,dp[y+1][x+1]);
    dp[y][x] = matrix[y][x]+val;
    }
    }
    int res = INT_MAX;
    for(int x=0;x<n;++x) res = min(res,dp[0][x]);
    return res;
    }
    };

470.用 Rand7() 实现 Rand10()

题目

已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。

不要使用系统的 Math.random() 方法。

示例 1:

输入: 1
输出: [7]

示例 2:

输入: 2
输出: [8,4]

示例 3:

输入: 3
输出: [8,1,10]

提示:

  1. rand7 已定义。
  2. 传入参数: n 表示 rand10 的调用次数。

进阶:

  1. rand7()调用次数的 期望值 是多少 ?
  2. 你能否尽量少调用 rand7() ?

    思路

  3. 重复调用10次rand7(),这样能够生成1~70的随机数,接着对10取余即可。
  4. 基于公式
    $$(randX()-1)*randY()+randY()$$
    生成1~X*Y之内的随机数。
  • 为了减少对rand7()的调用,需要对不同的1~X*Y之内的随机数进行判断。
    • 首先生成1~49之间的数,如果在1~40之间,直接对10取余即可。
    • 如果在41~49之间,减去40后就是一个1~9之间的随机数,仍然调用以上公式,生成1~63之间的随机数。
      • 如果在1~60之间,直接对10取余即可。
      • 如果在61~63之间,减去60后就是一个1~3之间的随机数,仍然调用上述公式,生成一个1~21之间的随机数……
        到这里,如果想要继续嵌套可以,但是此时还没有生成的概率已经很小了。如果还没有生成直接在大循环中重新生成也可以。
  1. 其他思考
    我尝试将两种概率分布都转化为正态分布,
    $$\frac{(rand7()-mean(rand7())}{std(rand7()))}=\frac{(rand10()-mean(rand10())}{std(rand10())}$$
    之后移项的方式来进行生成,但是不知道为什么一直不对:
    $$\frac{(rand7()-mean(rand7())*std(rand10())}{std(rand7())}+mean(rand10()))=rand10()$$

代码

  1. 暴力法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // The rand7() API is already defined for you.
    // int rand7();
    // @return a random integer in the range 1 to 7

    class Solution {
    public:
    int rand10() {
    int n = 0;
    for(int i=0;i<10;i++) n+=rand7();
    return n%10+1;
    }
    };
  2. 公式法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // The rand7() API is already defined for you.
    // int rand7();
    // @return a random integer in the range 1 to 7

    class Solution {
    public:
    int rand10() {
    while(true){
    int num = (rand7()-1)*7+rand7();
    if(num<=40) return 1+num%10;
    num = (num-40-1)*rand7()+rand7();
    if(num<=60) return 1+num%10;
    num = (num-60-1)*rand7()+rand7();
    if(num<=20) return 1+num%10;
    }
    return -1;
    }
    };

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

题目

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

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

提示:

  • 0 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4

    思路

    其实除了暴力没有特别好的思路,后来看了看别人的题解,自己又写了一遍。

    整体思路

    对下标进行归并排序,并且在排序时记录下每个位置后面数字的个数

    详细思路

    首先对整个数组进行归并。
    在每个归并段内,通过比较nums[idx[i]]来对idx[i]进行归并排序。并且在排序出现:nums[idx[i]]>nums[idx[j]]的时候,在idx[i]这个位置对应的最终结果(也即比nums中下标为idx[i]的元素右侧比这个元素大的元素个数)上面,加上该归并段内比nums[idx]大的数的个数:e-j+1

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    class Solution {
    public:
    vector<int> countSmaller(vector<int>& nums) {
    int n = nums.size();
    vector<int> res(n,0),helper(n,0),idxs(n,0);
    for(int i=0;i<n;i++) idxs[i] = i;
    mergeAndSort(nums,res,idxs,helper,0,n-1);
    return res;
    }
    void mergeAndSort(vector<int>& nums,vector<int>& res,vector<int>& idxs,vector<int>& helper,int s,int e){
    if(s>=e) return;
    int mid = s+(e-s)/2;
    mergeAndSort(nums,res,idxs,helper,s,mid);
    mergeAndSort(nums,res,idxs,helper,mid+1,e);
    mergeAndCount(nums,res,idxs,helper,s,mid,e);
    }
    void mergeAndCount(vector<int>& nums,vector<int>& res,vector<int>& idxs,
    vector<int>& helper,int s,int mid,int e){
    for(int i=s;i<=e;i++){
    helper[i] = idxs[i];
    }
    int i=s,j=mid+1,idx=s;
    while(i<=mid&&j<=e){
    if(nums[helper[i]]<=nums[helper[j]]){
    idxs[idx++] = helper[j++];
    }else{
    res[helper[i]]+= e-j+1;
    idxs[idx++]=helper[i++];
    }
    }
    while(i<=mid){
    idxs[idx++] = helper[i++];
    }
    while(j<=e){
    idxs[idx++] = helper[j++];
    }
    }
    };

KMP简洁模板

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int strStr(string haystack, string needle) {
if(needle.size()==0) return 0;
int* nxt = new int[needle.length()];
getNext(needle,nxt);
for(int i=0,j=0;i<haystack.size();i++){
while(j>0&&haystack[i]!=needle[j]) j = nxt[j-1];
if(haystack[i]==needle[j]) j++;
if(j==needle.size()) return i-j+1;
}
return -1;
}
void getNext(string pat,int* nxt){
int pLen = pat.length();
nxt[0] = 0;
for(int i = 1,j = 0;i<pLen;i++){
while(j>0&&pat[i]!=pat[j]) j=nxt[j-1];
if(pat[i]==pat[j]) j++;
nxt[i] = j;
}
}

198.打家劫舍

题目

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

思路

动态规划入门:

dp[i]=max(dp[i-1],nums[i]+dp[i-2])

代码

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
int rob(vector<int>& nums) {
vector<int> dp(nums.size()+2,0);
for(int i=0;i<nums.size();i++){
dp[i+2] = max(nums[i] + dp[i],dp[i+1]);
}
return dp.back();
}
};

240-搜索二维矩阵 II

题目

编写一个高效的算法来搜索 *m* x *n* 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例 1:

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

示例 2:

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false

思路

  1. 线性搜索:

    从右上往左下搜索。如果比右上大,就去下一行。如果比下一行小,就去该行左侧。

  2. 二分搜索

    指定左右、上下边界,与边界中心进行比较。

    • 如果比边界中心小:去中心左侧和上侧搜索。
    • 如果比边界中心大:去中心右侧和下侧搜索。

代码

  1. 线性搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Solution {
    public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
    int y = 0,x = matrix[0].size()-1;
    for(;y<matrix.size()&&x>=0;){
    if(matrix[y][x]==target) return true;
    if(matrix[y][x]<target) y++;
    else x--;
    }
    return false;
    }
    }
  2. 二分搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Solution {
    public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
    int m = matrix.size(), n = matrix[0].size();
    return searchMatrix(matrix,target,0,m-1,0,n-1);
    }
    bool searchMatrix(vector<vector<int>>& matrix,int target,int ru,int rd,int cl,int cr){
    if(ru>rd||cl>cr) return false;
    int rm = (ru+rd)/2, cm = (cl+cr)/2;
    if(matrix[rm][cm]==target) return true;
    else if(matrix[rm][cm]>target){
    return searchMatrix(matrix,target,ru,rm-1,cl,cr)||
    searchMatrix(matrix,target,ru,rd,cl,cm-1);
    }else{
    return searchMatrix(matrix,target,ru,rd,cm+1,cr)||
    searchMatrix(matrix,target,rm+1,rd,cl,cr);
    }
    }
    };

1109-航班预订统计

题目

1109. 航班预订统计

难度中等 236

这里有 n 个航班,它们分别从 1n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firstilasti包含 firstilasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 : 10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]

示例 2:

输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]

提示:

  • 1 <= n <= 2 * 104
  • 1 <= bookings.length <= 2 * 104
  • bookings[i].length == 3
  • 1 <= firsti <= lasti <= n
  • 1 <= seatsi <= 104

思路

  • 暴力

    时间复杂度为O(N^2^)。

  • 观察实例:

    航班编号 1 2 3 4 5
    预订记录 1 : 10 10
    预订记录 2 : 20 20
    预订记录 3 : 25 25 25 25
    总座位数: 10 55 45 25 25

    如果是对于bookings或者n分别进行嵌套循环的话,复杂度也是O(N^2^),不可接受。因此思考O(N)的方法。

    对于firsti ,lasti ,可以发现每个seati都要加载firsti的位置,之后复制就可以了,在lasti之后的位置减去seati。因此可以先对数组进行遍历使得结果数组res在每个first出现的位置都加上seat,在每个last的后一个位置减去seat。最后进行前缀累加即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
vector<int> res(n+1,0);
for(int i=0;i<bookings.size();++i){
res[bookings[i][0]-1] += bookings[i][2];
res[bookings[i][1]] -= bookings[i][2];
}
for(int j=1;j<n;j++){
res[j] += res[j-1];
}
res.resize(n);
return res;
}
};

528-按权重随机选择

528. 按权重随机选择

给定一个正整数数组 w ,其中 w[i]

表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。

例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。

也就是说,选取下标 i 的概率为 w[i] / sum(w)

示例 1:

输入:
[“Solution”,”pickIndex”]
[[[1]],[]]
输出:
[null,0]
解释:
Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。

示例 2

输入:
[“Solution”,”pickIndex”,”pickIndex”,”pickIndex”,”pickIndex”,”pickIndex”]
[[[1,3]],[],[],[],[],[]]
输出:
[null,1,1,1,1,0]
解释:
Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。**

由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
……
诸若此类。

提示:

  • 1 <= w.length <= 10000
  • 1 <= w[i] <= 10^5
  • pickIndex 将被调用不超过 10000

思路

随机+二分查找

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
vector<int> presum;
Solution(vector<int>& w) {
presum.resize(w.size()+1,0);
for(int i=1;i<=w.size();i++) presum[i] = presum[i-1]+w[i-1];
}

int pickIndex() {
int rnum = rand()%presum.back()+1;
int left = 1, right = presum.size()-1,mid;
while(left<right){
mid=(left+right)/2;
if(presum[mid]>=rnum) right=mid;
else left=mid+1;
}
return right-1;
}
};

927-三等分

题目

927. 三等分

难度困难 51

给定一个由 01 组成的数组 A,将数组分成 3 个非空的部分,使得所有这些部分表示相同的二进制值。

如果可以做到,请返回任何 [i, j],其中 i+1 < j,这样一来:

  • A[0], A[1], ..., A[i] 组成第一部分;
  • A[i+1], A[i+2], ..., A[j-1] 作为第二部分;
  • A[j], A[j+1], ..., A[A.length - 1] 是第三部分。
  • 这三个部分所表示的二进制值相等。

如果无法做到,就返回 [-1, -1]

注意,在考虑每个部分所表示的二进制时,应当将其看作一个整体。例如,[1,1,0] 表示十进制中的 6,而不会是 3。此外,前导零也是被允许的,所以 [0,1,1][1,1] 表示相同的值。

示例 1:

输入:[1,0,1,0,1]
输出:[0,3]

示例 2:

输出:[1,1,0,1,1]
输出:[-1,-1]

提示:

  1. 3 <= A.length <= 30000
  2. A[i] == 0A[i] == 1

思路

每组1的数目肯定相等。于是先统计1的位置并保存为bit,之后看bit的长度是否大于3、是否能被3整除。

能被3整除的情况下,分成三分,每份为t。每一份中1肯定都是t个,前缀0可以忽略,因此应该看后缀0有多少。前两份的前缀0和后面的后缀0混合在一起,可以自由组合,因此先不看。只看第3份的后缀0。如果第3份的后缀0个数比前两份之间的都多,也是不行的。在第三分的后缀0个数小于1、2和2、3之间的0的个数的时候,分别从第1份最后一个1后面延伸相应个数的0、第2份最后一个1后面延伸相应个数的0,返回位置即为答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
vector<int> threeEqualParts(vector<int>& arr) {
vector<int> bit;
int len = arr.size();
for(int i=0;i<arr.size();i++)
if(arr[i]==1)
bit.push_back(i);
if(bit.size()%3) return {-1,-1};
if(bit.empty()) return {0,2};
int t = bit.size()/3;
int back_zero = len - 1 - bit.back();
if(back_zero>bit[2*t]-bit[2*t-1]-1||back_zero>bit[t]-bit[t-1]-1)
return {-1,-1};
vector<int> first(bit.begin(),bit.begin()+t);
vector<int> second(bit.begin()+t,bit.begin()+2*t);
vector<int> third(bit.begin()+2*t,bit.end());
for(int i=t-1;i>0;--i){
if(first[i]-first[i-1]!=second[i]-second[i-1]||
first[i]-first[i-1]!=third[i]-third[i-1])
return {-1,-1};
}
int i = bit[t-1]+back_zero;
int j = bit[2*t-1]+back_zero+1;
return {i,j};
}
};