算法部分
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.
- 等会十点半参加一下LeetCode 夜喵双周赛,明天会讲题解