Coding Practice,48天强训(25)

Topic 1:笨小猴(质数判断的几种优化方式,容器使用的取舍)

笨小猴__牛客网

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) 
{
    if(n <= 1) return false;
    if(n <= 3) return true;      // 2和3是质数
    if(n % 2 == 0 || n % 3 == 0) return false; // 排除2和3的倍数
    
    // 只需检查6n±1形式的因数
    for(int i = 5; i*i <= n; i += 6) 
    {
        if(n % i == 0 || n % (i+2) == 0) return false;
    }
    return true;
}

int main() 
{
    string s; cin >> s;
    int freq[26] = {0};
    for (char c : s) freq[tolower(c)-'a']++;
    
    int maxn = *max_element(freq, freq+26);
    int minn = 101;// 最小可能为0, 所以不能用*min_element处理,手写一个
    for (int n : freq) 
    {
        if (n > 0 && n < minn) minn = n;
    }

    int diff = maxn - minn;
    if (isPrime(diff)) cout << "Lucky Word\n" << diff;
    else cout << "No Answer\n0";
}

另外选取几种判断质数的方式,忘了就回来看看

朴素判断:

bool isPrime(int n) {
    if(n <= 1) return false;         // 1不是质数
    for(int i = 2; i < n; i++) {    // 检查2到n-1
        if(n % i == 0) return false; // 能被整除就不是
    }
    return true;
}

平方根判断:只需判断到平方根,因为如果n有因数,一定会被分解成<=根号n的两个因数

bool isPrime(int n) {
    if(n <= 1) return false;
    for(int i = 2; i*i <= n; i++) {  // i <= sqrt(n)
        if(n % i == 0) return false;
    }
    return true;
}

跳过偶数:除了2,所有偶数都不是质数

bool isPrime(int n) {
    if(n <= 1) return false;
    if(n == 2) return true;          // 单独处理2
    if(n % 2 == 0) return false;     // 排除其他偶数
    
    for(int i = 3; i*i <= n; i += 2) { // 只检查奇数
        if(n % i == 0) return false;
    }
    return true;
}

6n+(-)1法则:更高级的数学原理——所有质数(除了2和3)都符合6n±1的形式。即:质数只能是6n+1或6n-1

bool isPrime(int n) {
    if(n <= 1) return false;
    if(n <= 3) return true;      // 2和3是质数
    if(n % 2 == 0 || n % 3 == 0) return false; // 排除2和3的倍数
    
    // 只需检查6n±1形式的因数
    for(int i = 5; i*i <= n; i += 6) {
        if(n % i == 0 || n % (i+2) == 0) 
            return false;
    }
    return true;
}

Topic 2:主持人调度(一)(贪心)

主持人调度(一)_牛客题霸_牛客网

#include <memory>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param schedule int整型vector<vector<>> 
     * @return bool布尔型
     */

    struct compare
    {
        bool operator()(const vector<int>& a, const vector<int>& b)// 写个比较用的仿函数
        {
            return a[1] < b[1]; // a 的结束时间(数组第二个元素) < b 的结束时间,则 a 排在前面
        }
    };

    bool hostschedule(vector<vector<int> >& schedule) 
    {
        int cl = 0;
        vector<int> res(schedule.size() * 2);
        sort(schedule.begin(), schedule.end(), compare());// 对原始数据排序,利用仿函数规则
        for(const auto& s : schedule)
        {
            for(const auto& e : s)
            {
                if(e >= cl) 
                {
                    res.push_back(e);
                    cl = e;
                }
                else return false;
            }
        }
        return true;
    }
};

初步考虑是这样,不过空间上还能优化,不用输出,不用储存,所以可以考虑不用额外的res,直接用下标+[]实现比对

#include <memory>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param schedule int整型vector<vector<>> 
     * @return bool布尔型
     */

    struct compare
    {
        bool operator()(const vector<int>& a, const vector<int>& b)// 写个比较用的仿函数
        {
            return a[1] < b[1]; // a 的结束时间(数组第二个元素) < b 的结束时间,则 a 排在前面
        }
    };

    bool hostschedule(vector<vector<int> >& schedule) 
    {
        sort(schedule.begin(), schedule.end(), compare());// 对原始数据排序,利用仿函数规则
        int cl = schedule[0][1];// 最早那个活动的结束时间
        for(int i = 1; i < schedule.size(); ++i)// 从第二次活动开始遍历其结束时间
        {
            if(schedule[i][0] < cl) return false;// 本次活动开始时间和上一次结束时间对比,<意味着有时间冲突,return false
            cl = schedule[i][1];
        }
        return true;
    }  
};

Topic 3:分割等和子集

分割等和子集_牛客题霸_牛客网

其实是01背包的一个变体,推荐做这题之前先去把01背包和滚动数组的两题先写了

带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili

关于01背包的dp[i][j]的dp含义我觉得卡尔讲的还不是特别清楚,这里我选取了另一位老师的解法

这个我觉得相对清晰,在这题中,dp[i][j]表示从0-i个数中选出的元素,最后总和为j

然后如果是有重量和价值的01背包,dp[i][j]就表示从0-i个物品中选出总重为j的物品,并且这些物品有最多的价值

所以dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])就可以理解成,最后的i我不选,因为我之前i-1时,背包容量j已经凑够了,所以是dp[i - 1][j]。最后的i我选,意味着j还没凑够,所以j此时就是j - weight[i],刚好还能容纳最后那个i,此时再加上它的价值就是dp[i - 1][j - weight[i]] + value[i]

然后再用滚动数组将二维压缩成一维,以示例1为例,以下是滚动数组的模拟过程,实际上就是用一个一维数组不断刷新并累加数据,这里注意滚动数组一定要倒着遍历,从前遍历的数据会影响后续的结果,会重复相加;

我既是现在的我,也是下一次行动中之前的我

#include <iostream>
#include <vector>
using namespace std;

int main() 
{
    // 抽象为01背包问题,题目中数组元素相当于物品,其重量和价值都是元素的值
    // 抽出的数之和 = 剩下数之和 = 总数/2 = 背包容量
    // 如果找到有物品能够装满背包容量,也就意味着这个数组可以取出若干个数,使得取出的数之和和剩下的数之和相同
    // 状态转移方程:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
    //             dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])  重量和价值都是元素的值
    // 一维记得要倒序遍历,两层循环不能颠倒,先物品后重量,二维无所谓
    int n, sum = 0; cin >> n;
    vector<int> nums(n);
    for(int i = 0; i < n; ++i) 
    {
        cin >> nums[i];
        sum += nums[i]; // 读取数组元素并计算总和sum
    }

    if (sum % 2 != 0) // 如果总和sum为奇数,直接输出false,因为无法均分
    {
        cout << "false";
        return 0;
    }

    int target = sum / 2; // 计算目标和target,即总和的一半
    vector<int> dp(target + 1, 0);
    // 初始化动态规划数组dp,dp[j]表示容量为j的背包能装的最大价值
    // 这里价值和重量都是nums[i],所以dp[j]表示能否凑出和为j的子集
    for(int i = 0; i < nums.size(); i++) 
    {
        for(int j = target; j >= nums[i]; j--) // 倒序遍历,防止重复计算
        {
            dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);// 状态转移方程:不选择当前数字或选择当前数字
        }
    }
    if (dp[target] == target) cout << "true";// 如果dp[target]等于target,说明可以凑出和为target的子集
    else cout << "false";

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值