算法部分
1.Acwing 入门组每日一题
题目:开心的金明
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,…,jk,则所求的总和为:
v[j1]∗w[j1]+v[j2]∗w[j2]+…+v[jk]∗w[jk]
请你帮助金明设计一个满足要求的购物单。
输入格式
输入文件的第1行,为两个正整数N和m,用一个空格隔开。(其中N表示总钱数,m为希望购买物品的个数)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v和p。(其中v表示该物品的价格,p表示该物品的重要度)
输出格式
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(数据保证结果不超过100000000)。
数据范围
1≤N<30000,
1≤m<25,
0≤v≤10000,
1≤p≤5
输入样例:
1000 5
800 2
400 5
300 5
400 3
200 2
输出样例:
3900
题解:
经典的背包问题,简单DP,原本需要使用二维数组来做,可以优化到一维数组,不过优化后需要从后往前dp,优化后的代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 3e5 + 10;
int dp[MAXN], p[30], w[30];
int main(){
int n, m, res = -1;
cin >> n >> m;
for(int i = 1; i <= m; i ++)
cin >> p[i] >> w[i];
//遍历每个商品
for(int i = 1; i <= m; i ++){
//遍历每一种钱数,从后往前dp
for(int j = n; j >= p[i]; j --){
dp[j] = max(dp[j], dp[j - p[i]] + p[i] * w[i]);
}
}
cout << dp[n] << endl;
return 0;
}
2.Acwing 提高组每日一题
题目:K倍区间
给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K 倍区间的数目。
数据范围
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6
题解:
这题是蓝桥杯历年的省赛题,可以看出来蓝桥杯是暴力杯无疑了,O(n^3)可以暴力求解,两层for寻找区间的两个端点,内层for求区间的和,这样子在 1e5 的数据范围肯定是不能拿到满分的,我们可以使用前缀和来省去计算区间和的过程从而优化算法到O(n^2),但是在 1e5 的数据范围上也是不能通过所有样例的,有一种巧妙的做法可以优化到O(n):
a. sum(i, j) = sum(0, j) - sum(0, i)
一个区间的和可以转换为两个区间的差,这是前缀和的思想
b. 如果一个序列是K倍数,使用a条件 就转换为找到两个区间,他们的差是K的倍数
所以我们使用一个cnt数组记录区间模K的余数的次数,然后使用一次遍历即可得到答案。代码如下:
#include <iostream>
using namespace std;
const int MAXN = 1e5 + 10;
long long arr[MAXN], cnt[MAXN];
int main(){
int n, k, a;
long long ans = 0;
cin >> n >> k;
for(int i = 1; i <= n; i ++){
cin >> arr[i];
//得到前缀和,省去一层for
arr[i] += arr[i - 1];
}
cnt[0] = 1;
for(int i = 1; i <= n; i ++){
//更新答案,这里又省去了一层for循环
ans += cnt[arr[i] % k];
//更新cnt
cnt[arr[i] % k] ++;
}
cout << ans << endl;
return 0;
}
3.LeetCode 每日一题
题目:公平的糖果棒交换
爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。
因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)
返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。
如果有多个答案,你可以返回其中任何一个。保证答案存在。
示例 1:
输入:A = [1,1], B = [2,2]
输出:[1,2]
示例 2:
输入:A = [1,2], B = [2,3]
输出:[1,2]
示例 3:
输入:A = [2], B = [1,3]
输出:[2,3]
示例 4:
输入:A = [1,2,5], B = [2,4]
输出:[5,4]
提示:
1 <= A.length <= 10000
1 <= B.length <= 10000
1 <= A[i] <= 100000
1 <= B[i] <= 100000
保证爱丽丝与鲍勃的糖果总量不同。
答案肯定存在。
题解:
两层for会超时,所以采取空间换时间的做法,把算法复杂度降低到O(n)。先分别统计得到Alice和Bob的糖果总数sum1和sum2,然后只要交换差为 abs(sum1 - sum2) / 2 的这样两个糖果即可,所以只需要再次遍历Alice或者Bob的糖果,将另一个人的糖果加入到一个set中,这样子就可以在O(1)时间内判断这样的糖果存不存在,代码如下:
class Solution {
public:
vector<int> fairCandySwap(vector<int>& A, vector<int>& B) {
unordered_set<int> set;
int sum1 = 0, sum2 = 0, diff;
for(int i : A){
sum1 += i;
//建立集合
set.insert(i);
}
for(int i : B)
sum2 += i;
//交换的糖果差值为总和差的一半
diff = (sum2 - sum1) >> 1;
for(int i : B)
//如果找到了这样的糖果,则return
if(set.count(i - diff))
return {
i - diff, i};
return {
-1, -1};
}
};
4.LeetCode 最接近的三数之和
题目:
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
题解:
双指针,将复杂度降为O(n^2)后可以通过,使用双指针前提是数组需要排序,就和二分需要排序一样,AC代码如下:
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int ans, diff = INT_MAX;
sort(nums.begin(), nums.end());
for(int i = 0; i + 2 < nums.size(); i ++){
int le = i + 1, ri = nums.size() - 1;
while(le < ri){
int t = target - nums[le] - nums[ri] - nums[i];
if(abs(t) < diff)
diff = abs(t), ans = nums[i] + nums[le] + nums[ri];
if(t > 0)
++ le;
else
-- ri;
}
}
return ans;
}
};
5.ccf-csp2020真题
题目链接
ccf-csp考试前两题还是蛮简单的,这一题需要使用Hash + sort。主要理解题意,之后找到规律轻松AC,PS. 官网题目好像不支持unordered_xxx 系列。
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
map<int, int> m;
const int MAXN = 1e5 + 10;
struct node{
int val, a, b;
//重载 <
bool operator < (const node & t) const {
return val < t.val;
}
};
node arr[MAXN];
int cnt = 0;
int main(){
int n, a, b, ans = 0, y, tmp = 0;
cin >> n;
for(int i = 0; i < n; i ++){
cin >> a >> b;
//将int映射为更小的int ><
if(m.find(a) == m.end())
m[a] = cnt ++;
arr[m[a]].val = a;
if(b)
++ tmp, arr[m[a]].b ++;
else
arr[m[a]].a ++;
}
//排序
sort(arr, arr + cnt);
ans = tmp;
y = arr[0].val;
//一遍循环
for(int i = 1; i < cnt; i ++){
//上一个0的数目减去上一个1的个数
tmp += arr[i - 1].a - arr[i - 1].b;
if(tmp >= ans){
ans = tmp;
y = arr[i].val;
}
}
cout << y;
return 0;
}
书籍部分
图解TCP|IP 第一章 ✔
MYSQL必知必会 第九章 ✔
PS.
- 明天学习复习一下md,这样子写出来的博客更好看
- github电子书下载 :Click Here
- 明天把闫氏DP分析法看一下