LeetCode 第192场周赛 题解

a.重新排列数组

a.题目

给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,…,xn,y1,y2,…,yn] 的格式排列。
请你将数组按 [x1,y1,x2,y2,…,xn,yn] 格式重新排列,返回重排后的数组。

示例 1

输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7]
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]

示例 2

输入:nums = [1,2,3,4,4,3,2,1], n = 4
输出:[1,4,2,3,3,2,4,1]

示例 3

输入:nums = [1,1,2,2], n = 2
输出:[1,2,1,2]

提示

  • 1 <= n <= 500
  • nums.length == 2n
  • 1 <= nums[i] <= 10^3

a.分析

很明显我们可以直接通过公式得知xi和yi的位置在哪的
xi=i yi=i+n
所以直接按i的顺序把xi yi依次push进去就行了

总的时间复杂度为O(n)

a.参考代码

class Solution {
public:
    vector<int> shuffle(vector<int>& nums, int n) {
        vector<int> ans;
        for(int i=0;i<n;i++)
        {
            ans.push_back(nums[i]);	//xi
            ans.push_back(nums[i+n]);	//yi
        }
        return ans;
    }
};

b.数组中的 k 个最强值

b.题目

给你一个整数数组 arr 和一个整数 k 。
设 m 为数组的中位数,只要满足下述两个前提之一,就可以判定 arr[i] 的值比 arr[j] 的值更强:

  • |arr[i] - m| > |arr[j] - m|
  • |arr[i] - m| == |arr[j] - m|,且 arr[i] > arr[j]

请返回由数组中最强的 k 个值组成的列表。答案可以以 任意顺序 返回。
中位数 是一个有序整数列表中处于中间位置的值。形式上,如果列表的长度为 n ,那么中位数就是该有序列表(下标从 0 开始)中位于 ((n - 1) / 2) 的元素。

  • 例如 arr = [6, -3, 7, 2, 11],n = 5:数组排序后得到 arr = [-3, 2, 6, 7, 11] ,数组的中间位置为 m = ((5 - 1) / 2) = 2 ,中位数 arr[m] 的值为 6 。
  • 例如 arr = [-7, 22, 17, 3],n = 4:数组排序后得到 arr = [-7, 3, 17, 22] ,数组的中间位置为 m = ((4 - 1) / 2) = 1 ,中位数 arr[m] 的值为 3 。

示例 1

输入:arr = [1,2,3,4,5], k = 2
输出:[5,1]
解释:中位数为 3,按从强到弱顺序排序后,数组变为 [5,1,4,2,3]。最强的两个元素是 [5, 1]。[1, 5] 也是正确答案。
注意,尽管 |5 - 3| == |1 - 3| ,但是 5 比 1 更强,因为 5 > 1 。

示例 2

输入:arr = [1,1,3,5,5], k = 2
输出:[5,5]
解释:中位数为 3, 按从强到弱顺序排序后,数组变为 [5,5,1,1,3]。最强的两个元素是 [5, 5]。

示例 3

输入:arr = [6,7,11,7,6,8], k = 5
输出:[11,8,6,6,7]
解释:中位数为 7, 按从强到弱顺序排序后,数组变为 [11,8,6,6,7,7]。
[11,8,6,6,7] 的任何排列都是正确答案。

示例 4

输入:arr = [6,-3,7,2,11], k = 3
输出:[-3,11,2]

示例 5

输入:arr = [-7,22,17,3], k = 2
输出:[22,17]

提示

  • 1 <= arr.length <= 10^5
  • -10^5 <= arr[i] <= 10^5
  • 1 <= k <= arr.length

b.分析

先把中位数mid给找出来
这里注意要根据题目的公式sorted_arr[(n-1)/2]是中位数 偶数情况不是小数来的
那么这里可以直接对升序排序得到sorted_arr来得到中位数

得到中位数之后 其实对于arr来说 强与弱其实是个大小关系
你只需要根据这个大小关系来进行对arr排序就行

最后取前k个即可

总的时间复杂度排序用时O(nlogn)
其实可以直接在原数组进行两次排序都没有关系的
比赛时候找中位数我另外开了个数组

b.参考代码

class Solution {
public:
    vector<int> getStrongest(vector<int>& arr, int k) {
        vector<int> v=arr;	//找中位数的数组
        sort(v.begin(),v.end());
        int n=arr.size();
        int mid=v[(n-1)/2];
        sort(arr.begin(),arr.end(),[mid](int a,int b)->bool{
            if(abs(a-mid)==abs(b-mid)) return a>b;
            return abs(a-mid)>abs(b-mid);
        });		//lambda表达式 根据题目的大小关系排序
        vector<int> ans;
        for(int i=0;i<k;i++)ans.push_back(arr[i]);	//前k个
        return ans;
    }
};

c.设计浏览器历史记录

c.题目

你有一个只有一个标签页的 浏览器 ,一开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。
请你实现 BrowserHistory 类:

  • BrowserHistory(string homepage) ,用 homepage 初始化浏览器类。
  • void visit(string url) 从当前页跳转到访问页面 url 。执行此操作会把浏览历史前进的记录全部删除。
  • string back(int steps) 在浏览历史中后退 steps 步。如果你只能在浏览历史中后退至多 x 步且 steps > x ,那么你只后退 x 步。请返回后退 至多 steps 步以后的 url 。
  • string forward(int steps) 在浏览历史中前进 steps 步。如果你只能在浏览历史中前进至多 x 步且 steps > x ,那么你只前进 x 步。请返回前进 至多 steps步以后的 url 。

示例

输入
[“BrowserHistory”,“visit”,“visit”,“visit”,“back”,“back”,“forward”,“visit”,“forward”,“back”,“back”]
[[“leetcode.com”],[“google.com”],[“facebook.com”],[“youtube.com”],[1],[1],[1],[“linkedin.com”],[2],[2],[7]]
输出
[null,null,null,null,“facebook.com”,“google.com”,“facebook.com”,null,“linkedin.com”,“google.com”,“leetcode.com”]
解释
BrowserHistory browserHistory = new BrowserHistory(“leetcode.com”);
browserHistory.visit(“google.com”); // 你原本在浏览 “leetcode.com” 。访问 “google.com”
browserHistory.visit(“facebook.com”); // 你原本在浏览 “google.com” 。访问 “facebook.com”
browserHistory.visit(“youtube.com”); // 你原本在浏览 “facebook.com” 。访问 “youtube.com”
browserHistory.back(1); // 你原本在浏览 “youtube.com” ,后退到 “facebook.com” 并返回 “facebook.com”
browserHistory.back(1); // 你原本在浏览 “facebook.com” ,后退到 “google.com” 并返回 “google.com”
browserHistory.forward(1); // 你原本在浏览 “google.com” ,前进到 “facebook.com” 并返回 “facebook.com”
browserHistory.visit(“linkedin.com”); // 你原本在浏览 “facebook.com” 。 访问 “linkedin.com”
browserHistory.forward(2); // 你原本在浏览 “linkedin.com” ,你无法前进任何步数。
browserHistory.back(2); // 你原本在浏览 “linkedin.com” ,后退两步依次先到 “facebook.com” ,然后到 “google.com” ,并返回 “google.com”
browserHistory.back(7); // 你原本在浏览 “google.com”, 你只能后退一步到 “leetcode.com” ,并返回 “leetcode.com”

提示

  • 1 <= homepage.length <= 20
  • 1 <= url.length <= 20
  • 1 <= steps <= 100
  • homepage 和 url 都只包含 ‘.’ 或者小写英文字母。
  • 最多调用 5000 次 visit, back 和 forward 函数。

c.分析

后退这个操作很容易就能想到的操作
而前进就很容易想到反向栈的操作
所以结合起来就是用的对顶栈来模拟

栈1表示当前和历史 栈2表示可以前进的

  • 新进一个网页 直接压入栈1 并且清空栈2
  • 后退网页 不断把栈1的网页弹出到栈2 (因为可以前进回去)直到steps完成或栈1只剩最后一个
  • 前进网页 不断把栈2的网页弹出到栈1 直到steps完成或者栈2空了
  • 返回的网页永远都是栈1的栈顶

时间复杂度大概是前进后退每次都要进行O(steps)次移动元素
访问的话为O(1)

c.参考代码

class BrowserHistory {
public:
    stack<string> stk1,stk2;
    BrowserHistory(string homepage) {
        stk1.push(homepage);	//初始
    }
    void visit(string url) {
        stk1.push(url);	//进栈
        while(stk2.size())stk2.pop();	//清空前进栈 栈没有clear()
    }
    string back(int steps) {
        while(stk1.size()>1&&steps--)	//后退至多steps
        {
            stk2.push(stk1.top());
            stk1.pop();
        }
        if(stk1.size())return stk1.top();
        else return "";	//应该不会有这种调用情况
    }
    string forward(int steps) {
        while(stk2.size()&&steps--)	//前进至多steps
        {
            stk1.push(stk2.top());
            stk2.pop();
        }
        if(stk1.size())return stk1.top();
        else return "";	//应该不会有这种调用情况
    }
};

d.给房子涂色 III

d.题目

在一个小城市里,有 m 个房子排成一排,你需要给每个房子涂上 n 种颜色之一(颜色编号为 1 到 n )。有的房子去年夏天已经涂过颜色了,所以这些房子不需要被重新涂色。
我们将连续相同颜色尽可能多的房子称为一个街区。(比方说 houses = [1,2,2,3,3,2,1,1] ,它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}]。)
给你一个数组 houses ,一个 m * n 的矩阵 cost 和一个整数 target ,其中:

  • houses[i]:是第 i 个房子的颜色,0 表示这个房子还没有被涂色。
  • cost[i][j]:是将第 i 个房子涂成颜色 j+1 的花费。

请你返回最小总花费,使得每个房子都被涂色后,恰好组成 target 个街区,如果没有涂色方案,请返回 -1 。

示例 1

输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
输出:9
解释:房子涂色方案为 [1,2,2,1,1]
此方案包含 target = 3 个街区,分别是 [{1}, {2,2}, {1,1}]。
涂色的总花费为 (1 + 1 + 1 + 1 + 5) = 9。

示例 2

输入:houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
输出:11
解释:有的房子已经被涂色了,在此基础上涂色方案为 [2,2,1,2,2]
此方案包含 target = 3 个街区,分别是 [{2,2}, {1}, {2,2}]。
给第一个和最后一个房子涂色的花费为 (10 + 1) = 11。

示例 3

输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[1,10],[10,1],[1,10]], m = 5, n = 2, target = 5
输出:5

示例 4

输入:houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3
输出:-1
解释:房子已经被涂色并组成了 4 个街区,分别是 [{3},{1},{2},{3}] ,无法形成 target = 3 个街区。

提示

  • m == houses.length == cost.length
  • n == cost[i].length
  • 1 <= m <= 100
  • 1 <= n <= 20
  • 1 <= target <= m
  • 0 <= houses[i] <= n
  • 1 <= cost[i][j] <= 10^4

d.分析

这种一看就是dp的最小花费问题
而且是线性dp 因为刷墙可以从左到右依次遍历观察(街区是否增加只和它的上一个有关)
首先我们来规定状态:
dp[i][j][k]表示为

  • 现在遍历到第i个房子(前面1~i-1)都遍历过了
  • 你打算把这个刷成j这种颜色
  • 现在你想它有k个街区

最小花费

也就是说这些状态都是子问题的答案

先上推导出来的状态转移方程:
dp[i][j][k]=min(dp[i-1][0~n][j==jj?k:k-1])+cost[i][j] 其中jj为0~n

是什么意思呢?
首先我们要算出某个状态ijk时候最小花费
只需要看下前面的那个房子涂的所有颜色哪个最小即可
所以dp[i-1][0~n][]应该是很容易理解的
重点在k这里 因为我们是固定街区的值的 而街区的值和上一个有关 具体表现为如何和上一个颜色一样就街区数一样 不一样就当前街区数会+1
因此就有了dp[][][j==jj?k:k-1]

如果当前房子已经涂过颜色 那么自然是不需要选择j是多少 j就是houses[i] 而且注意不需要花费
那么这个房子的作用就是起到了过滤其他状态 会把其他状态设置为非法(代码具体表现为答案值为无穷大)

初始化小细节:
初始化dp的时候应该只把dp[0][0~j][0]设置为0
这里i从1开始 方便转移 也就是i==0是特殊无意义状态
因为只有第一个房子允许从k==0转移过来

总的时间复杂度是O(mn^3)

d.参考代码

class Solution {
public:
    int dp[105][25][105];	//dp状态
    int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
        memset(dp,0x3f,sizeof(dp));	//全部初始化为非法
        for(int i=0;i<n;i++)dp[0][i][0]=0;	//只允许i==1从k==0转移过来
        for(int i=1;i<=m;i++)
            for(int j=0;j<n;j++)
                for(int k=1;k<=target;k++)
                {
                    if(houses[i-1]){	//如果已经涂色了
                        if(j==houses[i-1]-1)	//只有j和这个颜色相同才是合法的 否则不填充这个状态
                            for(int jj=0;jj<n;jj++)
                                dp[i][j][k]=min(dp[i][j][k],dp[i-1][jj][jj==j?k:k-1]);
                        continue;
                    }
                    else{
                        for(int jj=0;jj<n;jj++)	//遍历前一个的所有颜色情况
                            dp[i][j][k]=min(dp[i][j][k],dp[i-1][jj][jj==j?k:k-1]+cost[i-1][j]);
                    }
                }
        int ans=0x3f3f3f3f;
        for(int j=0;j<n;j++)ans=min(ans,dp[m][j][target]);	//找最小的答案 我们不知道最后填哪个颜色更优
        return ans==0x3f3f3f3f?-1:ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值