寒假CS每日打卡 Feb.7th


算法部分

1.Acwing 入门组每日一题
题目:陶陶摘苹果

陶陶家的院子里有一棵苹果树,每到秋天树上就会结出10个苹果。
苹果成熟的时候,陶陶就会跑去摘苹果。
陶陶有个30厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 
现在已知10个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。
假设她碰到苹果,苹果就会掉下来。

输入格式
输入文件包括两行数据。
第一行包含10个100到200之间(包括100和200)的整数(以厘米为单位)分别表示10个苹果到地面的高度,两个相邻的整数之间用一个空格隔开。
第二行只包括一个100到120之间(包含100和120)的整数(以厘米为单位),表示陶陶把手伸直的时候能够达到的最大高度。

输出格式
输出文件包括一行,这一行只包含一个整数,表示陶陶能够摘到的苹果的数目。

输入样例:
100 200 150 140 129 134 167 198 200 111
110
输出样例:
5

题解:
  签到题。

代码:

#include <iostream>

using namespace std;

int main(){
    
    
    int arr[10], ans = 0, he;

    for(int i = 0; i < 10; i ++)
        cin >> arr[i];
    cin >> he;
    he += 30;
    for(int i = 0; i < 10; i ++)
        if(arr[i] <= he)
            ++ ans;

    cout << ans << endl;
    return 0;
}

2.Acwing 提高组每日一题
题目:关押罪犯

S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为1~N。
他们之间的关系自然也极不和谐。
很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。
我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。
如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。
公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。
他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。
假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入格式
第一行为两个正整数 N 和 M,分别表示罪犯的数目以及存在仇恨的罪犯对数。
接下来的 M 行每行为三个正整数aj,bj,cj,表示aj号和bj号罪犯之间存在仇恨,其怨气值为cj。
数据保证1≤aj<bj<N,0<cj≤109 且每对罪犯组合只出现一次。

输出格式
输出共1行,为 Z 市长看到的那个冲突事件的影响力。
如果本年内监狱中未发生任何冲突事件,请输出0。

扫描二维码关注公众号,回复: 12472209 查看本文章

数据范围
N≤20000,M≤100000

输入样例:
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出样例:
3512

题解:
  贪心 + 并查集。题目要求最大的破坏最小,所以将数据从大到小排序后依次判断条件是否成立,如果不成立就输出答案,否则一直遍历。利用并查集查看罪犯的关系,不过本题的并查集做法不一般,引入了“虚节点”,可以参考这篇题解

代码:

#include <iostream>
#include <algorithm>

using namespace std;

struct node {
    
    
    int a, b, c;
    //按照破坏从大到小排序
    bool operator < (const node &t) const {
    
    
        return c > t.c;
    }
};

const int N = 20010, M = 100010;
int pre[2 * N];
node arr[M];

int find(int a){
    
    
    return a == pre[a] ? a : pre[a] = find(pre[a]);
}

int main(){
    
    
    int n, m, ans = 0;

    cin >> n >> m;

    for(int i = 1; i <= 2 * n; i ++)
        pre[i] = i;

    for(int i = 0; i < m; i ++)
        cin >> arr[i].a >> arr[i].b >> arr[i].c;
	//排序
    sort(arr, arr + m);

    for(int i = 0; i < m; i ++){
    
    
        int x = find(arr[i].a);
        int y = find(arr[i].b);
        //cout << x << " " << y << endl;
        if(x == y){
    
    
            ans = arr[i].c;
            break;
        }
        //x的pre为 b + n ;y的pre为 a + n
        pre[y] = find(arr[i].a + n);
        pre[x] = find(arr[i].b + n);
    }
    cout << ans << endl;
    return 0;
}

3.LeetCode 每日一题
题目: 非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

示例 1:
输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例 2:
输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

说明:
1 <= n <= 10 ^ 4

  • 10 ^ 5 <= nums[i] <= 10 ^ 5

题解:
  模拟题,需要细心,当nums[i] > nums[i + 1] 时有两个做法:a、将nums[i]贪心地变小为nums[i + 1] b、将nums[i + 1] 贪心地变大为nums[i] ,然后再去检查数组是否满足要求。ps. is_sorted() 函数为cpp封装好的,会线性的判断数组是否排好序。

代码:

class Solution {
    
    
public:
    bool checkPossibility(vector<int> &nums) {
    
    
        int n = nums.size();
        for (int i = 0; i < n - 1; ++i) {
    
    
            int x = nums[i], y = nums[i + 1];
            //不符合要求
            if (x > y) {
    
    
                //修改nums[i]
                nums[i] = y;
                if (is_sorted(nums.begin(), nums.end())) {
    
    
                    return true;
                }
                nums[i] = x; // 复原
                //修改nums[i + 1]
                nums[i + 1] = x;
                return is_sorted(nums.begin(), nums.end());
            }
        }
        return true;
    }
};

4.春招冲刺-最长回文子串
题目:

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。

示例 2:
输入:s = “cbbd”
输出:“bb”

示例 3:
输入:s = “a”
输出:“a”

示例 4:
输入:s = “ac”
输出:“a”

提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成

题解:
  DP,状态表示:bool dp[i][j] 表示第 i 到 j 子串是否为回文串,那么状态转移为 dp[i][j] = (s[i] == s[j]) & dp[i + 1][j - 1] ,也就是满足第 i 和 j 位置字符串相等,并且第i + 1 到 j - 1 位置的子串也是回文串。

代码:

class Solution {
    
    
public:
    string longestPalindrome(string s) {
    
    
        int len = s.length(), le = 0, ri = 0;
        vector<vector<bool>> dp(len, vector<bool>(len, false));
		//特殊处理长度为1 和 2的子串
        for(int i = 0; i < s.length(); i ++){
    
    
            dp[i][i] = true;
            if(i + 1 < len && s[i] == s[i + 1])
                le = i, ri = i + 1, dp[i][i + 1] = true;
        }
		//遍历子串长度
        for(int i = 2; i < len; i ++){
    
    
        	//遍历左端点
            for(int j = 0; j + i < len; j ++){
    
    
                //得到右端点
                int k = i + j;
                dp[j][k] = (s[j] == s[k]) & dp[j + 1][k - 1];
                if(dp[j][k] && i > ri - le)
                    le = j, ri = k;
            }
        }
        return s.substr(le, ri - le + 1);
    }
};

5.LC45场双周赛 - 唯一元素的和
题目:

给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。
请你返回 nums 中唯一元素的 和 。

示例 1:
输入:nums = [1,2,3,2]
输出:4
解释:唯一元素为 [1,3] ,和为 4 。

示例 2:
输入:nums = [1,1,1,1,1]
输出:0
解释:没有唯一元素,和为 0 。

示例 3 :
输入:nums = [1,2,3,4,5]
输出:15
解释:唯一元素为 [1,2,3,4,5] ,和为 15 。

提示:
1 <= nums.length <= 100
1 <= nums[i] <= 100

题解:
  先用哈希表存储出现次数,然后再遍历哈希表,累加那些只出现一次的数字。

代码:

class Solution {
    
    
public:
    int sumOfUnique(vector<int>& nums) {
    
    
        vector<int> cnt(110, 0);
        int ans = 0;
        
        for(int i : nums)
            cnt[i] ++;
        for(int i = 0; i < cnt.size(); i ++){
    
    
            if(cnt[i] == 1){
    
    
                ans += i;
            }
        }
        return ans;
    }
};

6.LC45场双周赛 - 任意子数组和的绝对值的最大值
题目:

给你一个整数数组 nums 。一个子数组 [numsl, numsl+1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl + numsl+1 + … + numsr-1 + numsr) 。
请你找出 nums 中 和的绝对值 最大的任意子数组(可能为空),并返回该 最大值 。
abs(x) 定义如下:
如果 x 是负整数,那么 abs(x) = -x 。
如果 x 是非负整数,那么 abs(x) = x 。

示例 1:
输入:nums = [1,-3,2,3,-4]
输出:5
解释:子数组 [2,3] 和的绝对值最大,为 abs(2+3) = abs(5) = 5 。

示例 2:
输入:nums = [2,-5,1,-4,3,-2]
输出:8
解释:子数组 [-5,1,-4] 和的绝对值最大,为 abs(-5+1-4) = abs(-8) = 8 。

提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104

题解:
  绝对值最大,也就是求的的值可能为正也可能为负,分别考虑这两种情况,即可转换为最大连续子数组的和,也就是贪心的做法。

代码:

class Solution {
    
    
public:
    int maxAbsoluteSum(vector<int>& nums) {
    
    
        int ans = 0, ri = 0, tmp = 0;
        //先考虑子数组和为正的情况
        while(ri < nums.size()){
    
    
        	//tmp < 0 不可能为最优解,就不选取之前的数字,从当前位置开始
            if(tmp < 0)
                tmp = 0;
            tmp += nums[ri];
            ans = max(ans, abs(tmp));
            ++ ri;
        }
        
        ri = 0;
        tmp = 0;
        //考虑为负数的情况
        while(ri < nums.size()){
    
    
        	//同理大于0也不选取
            if(tmp > 0)
                tmp = 0;
            tmp += nums[ri];
            ans = max(ans, abs(tmp));
            ++ ri;
        }
        return ans;
    }
};

7.LC45场双周赛 - 删除字符串两端相同字符后的最短长度
题目:

给你一个只包含字符 ‘a’,‘b’ 和 ‘c’ 的字符串 s ,你可以执行下面这个操作(5 个步骤)任意次:
选择字符串 s 一个 非空 的前缀,这个前缀的所有字符都相同。
选择字符串 s 一个 非空 的后缀,这个后缀的所有字符都相同。
前缀和后缀在字符串中任意位置都不能有交集。
前缀和后缀包含的所有字符都要相同。
同时删除前缀和后缀。
请你返回对字符串 s 执行上面操作任意次以后(可能 0 次),能得到的 最短长度 。

示例 1:
输入:s = “ca”
输出:2
解释:你没法删除任何一个字符,所以字符串长度仍然保持不变。

示例 2:
输入:s = “cabaabac”
输出:0
解释:最优操作序列为:

  • 选择前缀 “c” 和后缀 “c” 并删除它们,得到 s = “abaaba” 。
  • 选择前缀 “a” 和后缀 “a” 并删除它们,得到 s = “baab” 。
  • 选择前缀 “b” 和后缀 “b” 并删除它们,得到 s = “aa” 。
  • 选择前缀 “a” 和后缀 “a” 并删除它们,得到 s = “” 。

示例 3:
输入:s = “aabccabba”
输出:3
解释:最优操作序列为:

  • 选择前缀 “aa” 和后缀 “a” 并删除它们,得到 s = “bccabb” 。
  • 选择前缀 “b” 和后缀 “bb” 并删除它们,得到 s = “cca” 。

提示:
1 <= s.length <= 105
s 只包含字符 ‘a’,‘b’ 和 ‘c’ 。

题解:
  就是从两端同时删除相同的字符串,简单模拟即可。

代码:

class Solution {
    
    
public:
    int minimumLength(string s) {
    
    
        int le = 0, ri = s.length() - 1;
        while(le < ri && s[le] == s[ri]){
    
    
            while(le + 1 < ri && s[le] == s[le + 1])
                ++ le;
            ++ le;
            while(le + 1 < ri && s[ri] == s[ri - 1])
                -- ri;
            -- ri;
        }
        return ri - le + 1;
    }
};

8.最多可以参加的会议数目 II
题目:

给你一个 events 数组,其中 events[i] = [startDayi, endDayi, valuei] ,表示第 i 个会议在 startDayi 天开始,第 endDayi 天结束,如果你参加这个会议,你能得到价值 valuei 。同时给你一个整数 k 表示你能参加的最多会议数目。
你同一时间只能参加一个会议。如果你选择参加某个会议,那么你必须 完整 地参加完这个会议。会议结束日期是包含在会议内的,也就是说你不能同时参加一个开始日期与另一个结束日期相同的两个会议。
请你返回能得到的会议价值 最大和 。

示例 1:
在这里插入图片描述
输入:events = [[1,2,4],[3,4,3],[2,3,1]], k = 2
输出:7
解释:选择绿色的活动会议 0 和 1,得到总价值和为 4 + 3 = 7 。

示例 2:
在这里插入图片描述
输入:events = [[1,2,4],[3,4,3],[2,3,10]], k = 2
输出:10
解释:参加会议 2 ,得到价值和为 10 。
你没法再参加别的会议了,因为跟会议 2 有重叠。你 不 需要参加满 k 个会议。

示例 3:
在这里插入图片描述
输入:events = [[1,1,1],[2,2,2],[3,3,3],[4,4,4]], k = 3
输出:9
解释:尽管会议互不重叠,你只能参加 3 个会议,所以选择价值最大的 3 个会议。

提示:
1 <= k <= events.length
1 <= k * events.length <= 106
1 <= startDayi <= endDayi <= 109
1 <= valuei <= 106

题解:
  类似背包问题,使用动态规划解决,不过需要二分来优化,我的代码暴力然后TLE了,过了55/65个测试点,下面是别人的ac代码。

代码:

const int N = 1000010;

struct Q {
    
    
    int s, e, v;
    bool operator< (const Q& t) const {
    
    
        return e < t.e;
    }
}q[N];

class Solution {
    
    
public:
    int maxValue(vector<vector<int>>& events, int k) {
    
    
        int n = events.size();
        vector<vector<int>> f(n + 1, vector<int>(k + 1));
        for (int i = 1; i <= n; i ++ )
            q[i] = {
    
    events[i - 1][0], events[i - 1][1], events[i - 1][2]};
        sort(q + 1, q + n + 1);
        
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= k; j ++ ) {
    
    
                f[i][j] = f[i - 1][j];
                int l = 0, r = i - 1;
                while (l < r) {
    
    
                    int mid = l + r + 1 >> 1;
                    if (q[mid].e < q[i].s) l = mid;
                    else r = mid - 1;
                }
                f[i][j] = max(f[i][j], f[r][j - 1] + q[i].v);
            }
        
        return f[n][k];
    }
};

书籍部分

None


PS.

  1. 这次双周赛偏简单,对了3道,排名400,下次双周赛不参加了,写到12点后没有丝毫睡意,还会导致第二天不想a题 + a不出来题 ><,
  2. 明天复盘一下这周周赛题。

猜你喜欢

转载自blog.csdn.net/Raymond_YP/article/details/113747445