寒假CS每日打卡 Feb.6th


算法部分

1.Acwing 入门组每日一题
题目:数列

给定一个正整数k,把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是:
1,3,4,9,10,12,13,…
该序列实际上就是:30,31,30+31,32,30+32,31+32,30+31+32,…
请你求出这个序列的第N项的值(用10进制数表示)。 
例如,对于k=3,N=100,正确答案应该是981。

输入格式
输入文件只有1行,为2个正整数,用一个空格隔开:k N。

输出格式
输出文件为计算结果,是一个正整数(在所有的测试数据中,结果均不超过2.1∗109)。(整数前不要有空格和其他符号)。

数据范围
3≤k≤15,
10≤N≤1000
输入样例:
3 100
输出样例:
981

题解:
  找到规律即可,每一次新的幂出现的时候,和他之前所有出现的数字相加就是新得到的数字了。

代码:

#include <iostream>

using namespace std;

const int MAXN = 1010;
int arr[MAXN];

int main(){
    
    
    int k, n, tmp, cnt = 0;

    cin >> k >> n;
    tmp = 1;

    while(cnt < n - 1){
    
    
        arr[cnt ++] = tmp;
        //记录之前出现的数字个数
        int size = cnt - 1;
        for(int i = 0; i < size; i ++)
        	//相加就是新的数字
            arr[cnt ++] = arr[i] + tmp;
        //幂 + 1
        tmp *= k;
    }
    cout << arr[n - 1] << endl;
    return 0;
}

2.Acwing 提高组每日一题
题目:借教室

在大学期间,经常需要租借教室。
大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。
教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。
共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。 
我们假定,租借者对教室的大小、地点没有要求。
即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。 
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。
如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。
这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。 
现在我们需要知道,是否会有订单无法完全满足。
如果有,需要通知哪一个申请人修改订单。

输入格式
第一行包含两个正整数n,m,表示天数和订单的数量。 
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。 
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。 
每行相邻的两个数之间均用一个空格隔开。
天数与订单均用从1开始的整数编号。

输出格式
如果所有订单均可满足,则输出只有一行,包含一个整数0。
否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

数据范围
1≤n,m≤106,
0≤ri,dj≤109,
1≤sj≤tj≤n
输入样例:
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例:
-1
2

题解:
  二分 + 差分。二分用来降低时间按复杂度,题目要求输出第一个不满足条件的订单,当第 i + 1 个订单不满足条件时,其之后的所有订单都不满足条件;差分用来快速更新区间 [ l , r ] 之间的数值。

代码:

#include <iostream>

using namespace std;

const int MAXN = 1e6 + 10;
int room[MAXN], d[MAXN], s[MAXN], t[MAXN], n, m;
//差分数组
long long arr[MAXN];

bool check(int mid){
    
    
	//求得差分
    for(int i = 1; i <= n; i ++)
        arr[i] = room[i] - room[i - 1];
	//处理mid之前的所有订单,更新差分数组
    for(int i = 1; i <= mid; i ++){
    
    
        arr[s[i]] -= d[i];
        arr[t[i] + 1] += d[i];
    }
	//累加还原数组原本的值
    for(int i = 1; i <= n; i ++){
    
    
        arr[i] += arr[i - 1];
        //负数代表不合法
        if(arr[i] < 0)
            return false;
    }
    return true;
}

int main(){
    
    
    cin >> n >> m;

    for(int i = 1; i <= n; i ++)
        cin >> room[i];
    for(int j = 1; j <= m; j ++)
        cin >> d[j] >> s[j] >> t[j];

    if(check(m))
        cout << 0 << endl;
    else{
    
    
        int le = 1, ri = m, mid;
        //二分查找答案
        while(le < ri){
    
    
            mid = le + ri >> 1;
            if(check(mid))
                le = mid + 1;
            else
                ri = mid;
        }
        cout << -1 << endl << le << endl;
    }
    return 0;
}

3.LeetCode 每日一题
题目:可获得的最大点数

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。

示例 2:
输入:cardPoints = [2,2,2], k = 2
输出:4
解释:无论你拿起哪两张卡牌,可获得的点数总是 4 。

示例 3:
输入:cardPoints = [9,7,7,9,7,7,9], k = 7
输出:55
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。

示例 4:
输入:cardPoints = [1,1000,1], k = 1
输出:1
解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。

示例 5:
输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3
输出:202

提示:
1 <= cardPoints.length <= 10^5
1 <= cardPoints[i] <= 10^4
1 <= k <= cardPoints.length

题解:
  换个角度思考问题,数组大小为n,挑选k个数,求最大,就是剔除n - k个连续和最小的数字,这样子转换有什么好处呢,原本离散的区间现在被连续了,就可以使用滑动窗口(双指针)在O (n) 的复杂度解决问题了。不然从两端考虑很复杂,估计要dp。

代码:

class Solution {
    
    
public:
    int maxScore(vector<int>& cardPoints, int k) {
    
    
        k = cardPoints.size() - k;
        int ans = INT_MAX, tmp = 0, sum = 0;

        for(int i = 0; i < cardPoints.size(); i ++){
    
    
        	//求得数组总和
            sum += cardPoints[i];
            //维护区间和
            tmp += cardPoints[i];
            //大于等于k,左指针需要后移
            if(i >= k)
                tmp -= cardPoints[i - k];
            //大于等于k - 1 需要更新答案
            if(i >= k - 1)
                ans = min(ans, tmp);
        }
        //总和减去剔除的总和就是答案
        return sum - ans;
    }
};

4.春招冲刺-合并两个有序数组
题目:

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]

示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]

提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[i] <= 109

题解:
  从前往后来归并会占有原本nums1的元素,但是如果从后往前来归并就没有这个问题了。

代码:

class Solution {
    
    
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
    
    
        int pos1 = m - 1, pos2 = n - 1, pos = n + m - 1;
        
        while(pos1 >= 0 && pos2 >= 0){
    
    
            if(nums1[pos1] > nums2[pos2])
                nums1[pos --] = nums1[pos1 --];
            else
                nums1[pos --] = nums2[pos2 --];
        }
        //当nums1还未归并完全不需要处理,只有nums2还剩余的时候需要挪到nums中
        while(pos2 >= 0)
            nums1[pos --] = nums2[pos2 --];
    }
};

书籍部分

MYSQL必知必会 第17章 ✔


PS.

  1. 等会十点半参加一下LeetCode 夜喵双周赛,明天会讲题解

猜你喜欢

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