LeetCode第 227 场周赛题解

LeetCode第 227 场周赛题解

检查数组是否经排序和轮转得到

原题链接

https://leetcode-cn.com/problems/check-if-array-is-sorted-and-rotated/
在这里插入图片描述
在这里插入图片描述

解题思路

直接进行测试就行,因为数组的数据范围很小,直接进行 O ( N 2 ) O(N^2) O(N2算法即可,注意数组下标的求余

AC代码

class Solution {
public:
    
    bool Check(vector<int> a, vector<int> b, int k)
    {
        for (int i = 0; i < a.size(); i ++ )
        {
            static int u, v;
            if (i + k < a.size())   u = a[i + k];
            else    u = a[(i + k) % a.size()];
            v = b[i];
            if(u != v)
                return false;
        }
        return true;
    }
    bool check(vector<int>& nums) {
        vector<int> tmp = nums;
        sort(tmp.begin(), tmp.end());	// 记得先排个序
        for (int i = 0; i < tmp.size(); i ++ )
        {
            if (Check(tmp, nums, i))
                return true;
        }
        return false;
    }
};

移除石子的最大得分

原题链接

https://leetcode-cn.com/problems/maximum-score-from-removing-stones/
在这里插入图片描述
在这里插入图片描述

解题思路

首先,贪心的来讲,每一次操作为了保证最终得分最高,都会取当前最大和次最大,因为数据范围不大,也可以直接进行模拟.

  • 首先对,a, b, c进行排序,保证 a ≤ b ≤ c a\leq b\leq c abc
  • 最大值c和次最大值a进行操作,直到 a = b a=b a=b
  • 重复进行上述两个操作,直到 a , b , c a, b, c a,b,c至少 ∃ \exists 两个零
    下面是一种更为优秀的解法
    从不同堆石子中各取出一个,加一分,也就是说在遵循该规则的情况下,剩余的石子最少即可。
  • 倘若 a + b ≤ c a + b \leq c a+bc,分数最高为 a + b a + b a+b,因为 c c c堆石子中肯定会剩下的,最多取出的成对石子为 a + b a+b a+b
  • 倘若 a + b ≥ c a + b \geq c a+bc,那么肯定有一种方法,可以使得 a , b , c a, b, c a,b,c的绝对值最大差为 1 1 1,也就是说 a = b = c a=b=c a=b=c a = b = c − 1 a=b=c-1 a=b=c1,形成这种局面之后,我们可以通过每次取最大值和次最大值,依旧可以使得差的绝对值位置在 1 1 1之内。那么最后剩余的局面为 ( 0 , 0 , 0 ) ; ( 0 , 0 , 1 ) , ( 0 , 1 , 1 ) (0, 0, 0); (0, 0, 1), (0, 1, 1) (0,0,0);(0,0,1),(0,1,1)。其中 ( 0 , 1 , 1 ) ⇒ ( 0 , 0 , 0 ) (0, 1, 1) \Rightarrow(0, 0, 0) (0,1,1)(0,0,0)。最终结论可得为 r e s = ( a + b + c ) / 2 res = (a+b+c)/ 2 res=a+b+c/2

AC代码

模拟解法

class Solution {
public:
    int maximumScore(int a, int b, int c) {
        vector<int> t;  t.push_back(a);
        t.push_back(b), t.push_back(c);
        sort(t.begin(), t.end());
        if (t[2] >= t[0] + t[1])
            return t[0] + t[1];
        else
        {
            int ans = 0;
            int tmp;
            tmp = t[1] - t[0];
            ans += tmp;
            t[1] -= tmp;    t[2] -= tmp;
            while (true)
            {
                sort(t.begin(), t.end());
                if (t[0] == 0 && t[1] == 0) 
                    break;
                
                tmp = t[1] - t[0];
                if (tmp == 0)   // a = b && a >= 1 && b >= 1
                {
                    if (t[2] >= 2)
                    {
                        ans += 2;
                        t[2] -= 2, t[1] -= 1, t[0] -= 1;
                    }
                    else	// a = 1, b = 1, c = 1
                    {
                        ans += 1;
                        break;
                    }
                }
                else		// b > a直接取到 b = a
                {
                    tmp = t[1] - t[0];
                    ans += tmp;
                    t[1] -= tmp;    t[2] -= tmp;
                }
            }
            return ans;
        }
            
        
    }
};	

线性解法

class Solution {
public:
    int maximumScore(int a, int b, int c) {
        int d[] = {a, b, c};
        sort(d, d + 3);
        if (d[0] + d[1] <= d[2])    return d[0] + d[1];
        else    return (d[0] + d[1] + d[2]) / 2;
    }
};

构造字典序最大的合并字符串

原题链接

https://leetcode-cn.com/problems/largest-merge-of-two-strings/
在这里插入图片描述

在这里插入图片描述

解题思路

思路介绍

首先看数据范围, O ( N 2 ) O(N^2) O(N2)算法即可
对于两个字符串 w o r d 1 , w o r d 2 word1, word2 word1,word2,关键在于判断谁的当前首字符用于操作,放在 m e r g e merge merge串的末尾

  • 倘若 F i r s t w o r d 1 First_{word1} Firstword1为空(word1已经被选完),当然是选择 w o r d 2 word2 word2首字符
  • 倘若 F i r s t w o r d 2 First_{word2} Firstword2为空(word2已经被选完),当然是选择 w o r d 1 word1 word1首字符
  • 倘若 F i r s t w o r d 1 > F i r s t w o r d 2 First_{word1} > First_{word2} Firstword1>Firstword2那么,当然是选择 w o r d 1 word1 word1首字符
  • 同理,倘若 F i r s t w o r d 1 < F i r s t w o r d 2 First_{word1} < First_{word2} Firstword1<Firstword2那么,当然是选择 w o r d 2 word2 word2首字符
  • 倘若 F i r s t w o r d 1 = F i r s t w o r d 2 First_{word1} = First_{word2} Firstword1=Firstword2,那我们需要递归比较下面的字符。
思路证明

在这里插入图片描述

具体操作看下面的代码

AC代码

class Solution {
public:
    
    bool Check(string &word1, string &word2, int pre, int nxt)
    {
        if (pre == word1.size())    return false;
        else if (nxt == word2.size())   return true;
        
        if (word1[pre] > word2[nxt])    return true;
        else if (word1[pre] < word2[nxt])   return false;
        else return Check(word1, word2, pre + 1, nxt + 1);
    }
    string largestMerge(string word1, string word2) {
        string ret = "";
        int pre = 0, nxt = 0;
        while (pre < word1.size() || nxt < word2.size())
        {
            if (Check(word1, word2, pre, nxt))
            {
                ret += word1[pre];
                pre ++;
            }
            else
            {
                ret += word2[nxt];
                nxt ++;
            }
                
        }
        return ret;
    }
};

最接近目标值的子序列和

原题链接

https://leetcode-cn.com/problems/closest-subsequence-sum/
在这里插入图片描述
在这里插入图片描述

解题思路

首先,我们观察数据范围,发现 n u m . l e n g t h ≤ 40 num.length \leq 40 num.length40很有可能使用 d f s dfs dfs,但是在观察题目,暴力的情况是无法剪枝的,考虑一下将其进行一半分开 d f s dfs dfs前一半, d f s dfs dfs后一半,然后进行将dfs结果排序去重后进行二分,使其更加接近于 g o a l goal goal

AC代码

vector<int> ved1, ved2, num1, num2;
int sum = 0;
class Solution {
public:
    void dfs1(int cur)	// dfs num1部分
    {
        if (cur >= num1.size())
        {
            return;
        }
        
        sum += num1[cur];	// 选
        ved1.push_back(sum);	// 放入
        dfs1(cur + 1);
        
        sum -= num1[cur];	// 不选
        dfs1(cur + 1);
    }
    
    void dfs2(int cur)	// dfs num2部分
    {
        if (cur >= num2.size())
        {
            return;
        }
        
        sum += num2[cur];
        ved2.push_back(sum);
        dfs2(cur + 1);
        
        sum -= num2[cur];
        dfs2(cur + 1);
    }
    
    int minAbsDifference(vector<int>& nums, int goal) {
        int n = nums.size();
        num1.clear(), num2.clear(), ved1.clear(), ved2.clear();
        ved1.push_back(0);
        ved2.push_back(0);
        for (int i = 0; i < n / 2; i ++ )   num1.push_back(nums[i]);
        for (int i = n / 2; i < n; i ++ )   num2.push_back(nums[i]);
        // dfs
        sum = 0;	dfs1(0);
        sum = 0;	dfs2(0);
        
        // 排序去重
        sort(ved1.begin(), ved1.end());
        ved1.erase(unique(ved1.begin(), ved1.end()), ved1.end());
        sort(ved2.begin(), ved2.end());
        ved2.erase(unique(ved2.begin(), ved2.end()), ved2.end());
        /*
        for (auto i : ved1) cout << i << " ";   cout << endl;
        for (auto i : ved2) cout << i << " ";   cout << endl;
        */
        int l, r, mid, k;
        int res = 0x3f3f3f3f;
        for (auto x : ved1)
        {
        	// 对于每一个元素两次二分操作答案
            l = 0, r = ved2.size() - 1;
            k = goal - x;
            // x + y >= goal --> y >= goal - x (min)
            if (ved2[r] >= k)
            {
                while (l < r)
                {
                    mid = l + r >> 1;
                    if (ved2[mid] >= k)
                    {
                        r = mid;
                    }
                    else
                    {
                        l = mid + 1;
                    }
                    
                }
                res = min(res, abs(x + ved2[l] - goal));
            }
            
            // x + y <= goal --> y <= goal - x (max)
            l = 0, r = ved2.size() - 1;
            if (ved2[l] <= k)
            {
                while (l < r)
                {
                    mid = l + r + 1 >> 1;
                    if (ved2[mid] <= k)
                    {
                        l = mid;
                    }
                    else
                    {
                        r = mid - 1;
                    }
                    
                }
                res = min(res, abs(x + ved2[l] - goal));
            }
            
            if (res == 0)   break;
        }
        return res;
    }
};

小结

  • int d[] = {a, b, c}; 挺好使的
  • 证明的时候反证法加上调整法,直接整挺好

参考

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值