寒假CS每日打卡 Feb.1st


算法部分

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.

  1. 明天学习复习一下md,这样子写出来的博客更好看
  2. github电子书下载 :Click Here
  3. 明天把闫氏DP分析法看一下

猜你喜欢

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