寒假力扣周赛


大意:给定两个数组a,b,里面的元素都是1-6之间的数,每次操作可以对任意数组中的任意元素修改为1-6之间的数,问最少操作数使得两数组的元素和相等。
思路:假设a的和比b的和大,那先对a从大到小排序,对b从小到大排序,求出差值ca,l指向a中未修改且最靠前的元素,r指向b中未修改且最靠前的元素,贪心策略为把a中的元素尽量修改为1,b中的元素尽量修改为6,前提条件是先比较a[l]-1,6-a[r]的大小。
class Solution {
public:
    bool cmp(int a,int b)
    {
        return a > b;
    }
    vector<int>a,b;
    int minOperations(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        int tmp1 = 0,tmp2 = 0;
        if(n > m){
            if(n > m*6) return -1;
        }
        if(n < m){
            if(m > 6*n) return -1;
        }
        for(int i = 0; i < n; i++){
            tmp1 += nums1[i];
        }
        for(int i = 0; i < m; i++){
            tmp2 += nums2[i];
        }
        int ans = 0;
        int ca;
        if(tmp1 > tmp2){
            a = nums1;
            b = nums2;
        }
        else{
            a = nums2;
            b = nums1;
        }
        sort(a.begin(),a.end());
        sort(b.begin(),b.end());
        reverse(a.begin(),a.end());
        ca = abs(tmp1 - tmp2);
        int l = 0,r = 0,x,y;
        n = a.size();
        m = b.size();
        while(l < n && r < m){
            x = a[l]-1;
            y = 6-b[r];
            if(x < y){
                if(ca <= 0) return ans;
                ca -= y;
                r++;
                ans++;
            }
            else if(x > y){
                if(ca <= 0) return ans;
                ca -= x;
                l++;
                ans++;
            }
            else {
                if(ca <= 0) return ans;
                ca -= x;
                l++;
                ans++;
            }
            if(ca <= 0) return ans;
        }
         while(l < n){
            x = a[l]-1;
            if(ca <= 0) return ans;
                ca -= x;
                l++;
                ans++;
        }
        while(r < m){
            y = 6-b[r];
            if(ca <= 0) return ans;
            ca -= y;
            r++;
            ans++;
        }
        return ans;
    }
};


第229场周赛

3-执行乘法运算的最大分数:动态规划

大意:给定一个数组nums和另一个数组multipliers,第i次可以从nums中的左端或右端取一个数和multipliers中的第i个数相乘,问最后所有结果相加和最大是多少。
思路:要么从左取要么从右取,两种选择,数据范围不大,考虑动态规划做。
找状态:dp[i][j] 表示选第k个数是有i个左端点,j个右端点。假设i+j=k
状态转移:如果i=0,也就是全取的是右端点,当前只能取右端点,那么dp[i][k-j] = dp[i][k-j-1]+a[m-k+i]*b[k-1];
如果i=k,也就是全是做端点,当前只能取左端点,那么dp[i][k-i] = dp[i-1][k-i]+a[i-1]*b[k-1];
其它情况dp[i][k-i] = max(dp[i][k-i-1]+a[m-k+i]*b[k-1], dp[i-1][k-i]+a[i-1]*b[k-1]);两者取最大中的一个。
class Solution {
public:
    int a[100005],b[100005];
    int dp[2005][2005];
    int maximumScore(vector<int>& nums, vector<int>& multipliers) {
        int n = multipliers.size();
        int m = nums.size();
        for(int i = 0; i < nums.size(); i++) a[i] = nums[i];
        for(int i = 0; i < multipliers.size(); i++) b[i] = multipliers[i];
        int ans = -1e9;
        memset(dp,0,sizeof(dp));
        for(int k = 1; k <= n; k++){
            for(int i = 0; i <= k; i++){
                if(i == 0) dp[i][k-i] = dp[i][k-i-1]+a[m-k+i]*b[k-1];
                else if(i == k) dp[i][k-i] = dp[i-1][k-i]+a[i-1]*b[k-1];
                else{
                    dp[i][k-i] = max(dp[i][k-i-1]+a[m-k+i]*b[k-1], dp[i-1][k-i]+a[i-1]*b[k-1]);
                }
                if(k == n) ans = max(dp[i][k-i],ans);
            }
        }
        return ans;
    }
};

4-由子序列构造的最长回文串的长度:动态规划

大意:给定两个字符串s1,s2,从s1中取出一个非空子序列,再从s2中取出一个非空子序列,问连接后的字符串是回文串吗?最长是多少。
思路:直接将s1和s2先连起来,再求回文子序列的最长长度,注意一端要在s1内,另一端要在s2内。
class Solution {
public:
    int dp[2005][2005];
    int longestPalindrome(string word1, string word2) {
        string s = word1+word2;
        int n = s.size();
        int n1 = word1.size()-1;
        int n2 = word2.size()-1;
        int ans = 0;
        for(int i = n-1; i >= 0; i--){//由小区间扩展到大区间,所以倒过来。
           dp[i][i] = 1;
           for(int j = i+1; j < n; j++){
               if(s[i] == s[j]){
                   dp[i][j] = dp[i+1][j-1]+2;
                   if(i <= n1 && j > n1) ans = max(dp[i][j],ans);
               }
               else dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
           }
        }
        return ans;
    }
};



第227场周赛

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

大意:问是否存在k使得所有数循环移动(i+k)%nums.size()后,使得数组有序。
思路:暴力枚举原数组中的每个数,看看是否存在以某个数为起点使得数组有序。
class Solution {
public:
    bool check(vector<int>& nums) {
        int a[105];
        vector<int>num = nums;
        int t = nums[0],xb = 0;
        for(int i = 1; i < nums.size(); i++){
            if(nums[i] < t){
                xb = i;
                t = nums[i];
            }
        }
      //  cout << xb;
        for(int k = 0; k < num.size(); k++){
            int q = 0;
             for(int i = k; i < num.size(); i++){
            a[q++] = num[i];
        }
        for(int i = 0; i < k; i++){
            a[q++] = num[i];
        }
            int flag = 0;
        sort(nums.begin(),nums.end());
        for(int i = 0; i < nums.size(); i++){
          //  cout << a[i] << " ";
            if(a[i] != nums[i]) {
                flag = 1;
                break;
            }
        }
            
        if(!flag) return true;
        }
       return false;
    }
};

2-移除石子的最大得分

大意:给出三堆石子,每次从不同的两堆中个取走一个石子,并加一分,问最大得分是多少。
class Solution {
public:
    int maximumScore(int a, int b, int c) {
        int num[10];
        num[0] = a;
        num[1] = b;
        num[2] = c;
        sort(num,num+3);
        if(num[0]+num[1] < num[2]) return num[0]+num[1];
        if(num[0]+num[1] == num[2]) return num[2];
        return (num[0]+num[1]-num[2])/2+num[2];
    }
};

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

大意:给定两个字符串s1,s2,有两种操作,第一种是取s1的第一个字符加入merge尾部并从s1中删去,第二种是取s2的第一个字符加入merge尾部并从s2中删去,求merge最大字典序的字符串是多少。
思路:弄两个指针l1,l2分别指向s1,s2的第一个字符,判断s1[l1],s2[l2]大小,取最大的那个,如果相同,那么继续判断后面的字符,谁大就取谁。(注意边界如s1="ababa",s2="ababa",答案merge="ababababa")
class Solution {
public:
    string largestMerge(string word1, string word2) {
        int l1,l2,r1,r2;
        string ans;
        l1 = l2 = 0;
        int n1,n2;
        n1 = word1.size();
        n2 = word2.size();
        ans = "";
        while(l1 < n1 && l2 < n2){
            if(word1[l1] < word2[l2]){
                ans += word2[l2];
                l2++;
                continue;
            }
            if(word1[l1] > word2[l2]){
                ans += word1[l1];
                l1++;
                continue;
            }
            int t1,t2;
         //   cout << l1 << " " << l2 << endl;
            t1 = l1;
            t2 = l2;
            while(word1[t1] == word2[t2] && t1 < n1 && t2 < n2){
               // cout << word1[t1] << " " << word2[t2] << "\n";
                t1++;
                t2++;
            }
            if(t1 == n1 && t2 == n2){
                ans += word1[l1];
                l1++;
            }
            if(t1 == n1) word1 += "A";
            if(t2 == n2) word2 += "A";
            if(word1[t1] < word2[t2]){
                ans += word2[l2];
                l2++;
                continue;
            }
            if(word1[t1] > word2[t2]){
                ans += word1[l1];
                l1++;
            }
        }
        if(l1 < n1){
                for(int i = l1; i < n1; i++) ans += word1[i];
            }
            if(l2 < n2){
                for(int i = l2; i < n2; i++) ans += word2[i];
            }
        return ans;
    }
};

4-最接近目标值的子序列和:dfs+二分

大意:给定一组数,从中取若干个数,使得这些数的和和goal最接近。
思路:看到数据范围是40,又是取若干个数,首先想到的是dfs+剪枝,可是还是TLE,那就试着把范围缩小,将数组对半分成两个数组,对这两个数组dfs暴搜出所有和的情况,然后对其中一个数组排序,枚举另一个数组中的每一个数,因为要尽可能让lsum+rsum>=goal,所以在排好序的数组中查找第一个大于等于goal-rsum,还有可能在这个位置的前一个。
class Solution {
public:

    int n,g,xb;
    long long ans = 1e15;
    vector<int>num;
    vector<int>num1[5];
    void dfs(int cur,int en,int res,int bh)
    {
        if(cur > en) return ;
        num1[bh].push_back(res);
        for(int i = cur+1; i <= en; i++){  
            dfs(i,en,res+num[i],bh);
        }
    }

    int minAbsDifference(vector<int>& nums, int goal) {
        n = nums.size();
        num = nums;
        for(int i = 0; i <= n/2-1; i++){
            dfs(i,n/2-1,nums[i],1);
        }
        for(int i = n/2; i <= n-1; i++){
            dfs(i,n-1,nums[i],2);
        }
        sort(num1[2].begin(),num1[2].end());
        int ans = 1e9+7;
        for(int i = 0; i < num1[1].size(); i++){
        //    cout << num1[1][i] << "\n";
            int temp = goal-num1[1][i];
            int xb = lower_bound(num1[2].begin(),num1[2].end(),temp)-num1[2].begin();
        //    cout << xb << endl;
            ans = min(ans,abs(num1[1][i]-goal));
            ans = min(ans,abs(num1[2][xb]+num1[1][i]-goal));
            if(xb >= 1){
                ans = min(ans,abs(num1[2][xb-1]+num1[1][i]-goal));
            }
        }
        for(int i = 0; i < num1[2].size(); i++){
            ans = min(ans,abs(num1[2][i]-goal));
        }
        return min(ans,abs(goal));
    }
};

第45场双周赛

https://leetcode-cn.com/contest/biweekly-contest-45/

2-任意子数组和的绝对值的最大值

大意:给定一组数,问子数组中和的绝对值的最大值是多少。
思路:先求出nums的前缀和pre,然后对pre排序,如果pre中全是负数,那么取pre中最小数的绝对值,如果全是正数,那么取pre中最大的数,如果即有正又有负,取pre中最大值减去最小值作为答案。
class Solution {
public:
    int pre[100005];
    int maxAbsoluteSum(vector<int>& nums) {
        pre[0] = nums[0];
        for(int i = 1; i < nums.size(); i++){
            pre[i] = nums[i]+pre[i-1];
        }
        sort(pre,pre+nums.size());
        if(nums.size() == 1) return abs(nums[0]);
        if(pre[0] >= 0) return pre[nums.size()-1];
        if(pre[nums.size()-1] < 0) return abs(pre[0]);
        return max(abs(pre[nums.size()-1]-pre[0]),abs(pre[nums.size()-1]));
    }
};

3-删除字符串两端相同字符后的最短长度

class Solution {
public:
    int minimumLength(string s) {
        int l = 0,r = s.length()-1;
      //  if(r == 0) return 1;
        while(l <= r){
            if(l == r){
                if(s[r] == s[r+1]) return 0;
                return 1;
            }
            if(s[l] == s[r]){
                l++;
                r--;
            }
            else{
                if(l == 0 || r == s.length()-1) return s.length();
                if(s[l-1] == s[l]){
                    l++;
                }
                else if(s[r+1] == s[r]){
                    r--;
                }
                else{
                    break;
                }
            }
        }
        return r-l+1;
    }
};

4:最多可以参加的会议数目 II:动态规划+二分

思路:一看这种考虑选或不选,数据范围也不小的题,可以考虑用dp来解决。
先将会议按结束时间从小到大排序。
状态:dp[i][j]表示前i个会议中参加了j个的最大价值和。
状态转移:
1)第i个会议不参加,那么dp[i][j] = dp[i-1][j]。
3)参加第i个会议,从前i个会议中二分找到会议结束时间首先小于第i个会议开始时间的会议l,dp[i][j] = dp[l][j-1],为什么不是dp[i][j] = dp[i-1][j-1]呢,因为如果第i-1个会议和第i个会议冲突的话,答案会错误,也就是前面的状态对后面的状态有影响。
class Solution {
public:
    struct Node{
        int s,e,v;
        bool operator<(const Node &x)const{
            return x.e > e;
        }
    }node[1000005];
    int s[1000005],e[1000005],v[1000005];
    int maxValue(vector<vector<int>>& events, int k) {
        vector<int>dp[1000005];
        for(int i = 0; i < events.size(); i++)
            for(int j = 0; j <= k; j++) dp[i].push_back(0);
        for(int i = 0; i < events.size(); i++){
            node[i].s = events[i][0];
            node[i].e = events[i][1];
            node[i].v = events[i][2];
        }
        sort(node,node+events.size());
        dp[0][0] = 0;
        dp[0][1] = node[0].v;
        //cout <<node[2].e << endl;
        for(int i = 1; i < events.size(); i++){
            for(int j = 1; j <= k; j++){//不选
                dp[i][j] = max(dp[i][j],dp[i-1][j]);
            }
            int l = 0,r = i,ans = -1;
            while(l <= r){
                int mid = (l+r)>>1;
                if(node[mid].e < node[i].s){
                    ans = mid;
                    l = mid+1;
                }
                else r = mid-1;
            }
            if(ans == -1){
                dp[i][1] = max(dp[i][1],node[i].v);
                continue;
            }
            for(int j = 1; j <= k; j++){
                dp[i][j] = max(dp[i][j],dp[ans][j-1]+node[i].v);
            }
        }
        int  ans = 0;
        for(int j = 0; j <= k; j++){
            ans = max(ans,dp[events.size()-1][j]);
        }
        return ans;
    }
};











全部评论

相关推荐

10-30 10:16
南京大学 Java
龚至诚:给南大✌️跪了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务