笔试强训题(3)

1. Day13

1.1 牛牛冲钻五(模拟)

  1. 题目链接牛牛冲钻五
  2. 题目描述:

  1. 解法:
    • 算法思路:简单模拟题,属于每次笔试的热身题~
  2. C++ 算法代码:
#include <iostream>
#include <string>
using namespace std;

int t, n, k;
string s;

int fun()
{
    int ret = 0;
    for(int i = 0; i < s.size(); i++)
    {
        if(s[i] == 'L')
        {
            ret--;
        }
        else
        {
            if(i - 2 >= 0 && s[i - 1] == 'W' && s[i - 2] == 'W')
            {
                ret += k;
            }
            else
            {
                ret++;
            }
        }
    }
    
    return ret;
}

int main()
{   
    cin >> t;
    while(t--)
    {
        cin >> n >> k >> s;
        cout << fun() << endl;
    }
  
    return 0;
}

1.2 最长无重复子数组(滑动窗口)

  1. 题目链接NC41 最长无重复子数组
  2. 题目描述:

  1. 解法:
    • 算法思路:经典滑动窗口题目。
  2. C++ 算法代码:
class Solution {
public:
    int hash[100010] = {0};

    int maxLength(vector<int>& arr)
    {
        int ret = 1;
        int left = 0;
        int right = 0;
        while(right < arr.size())
        {
            hash[arr[right]]++;
            while(hash[arr[right]] > 1)
            {
                hash[arr[left]]--;
                left++;
            }

            ret = max(ret, right - left + 1);
            right++;
        }

        return ret;
    }
};

1.3 重排字符串(贪心 + 构造)

  1. 题目链接重排字符串
  2. 题目描述:

  1. 解法:
    • 算法思路:力扣有⼀道《距离相等的条形码》和这道题是差不多的~
  2. C++ 算法代码:
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
    int n = 0;
    cin >> n;
    string s;
    cin >> s;
    
    vector<int> hash(26, 0);    
    int maxcount = 0;
    int maxindex = 0;
    for(int i = 0; i < n; i++)
    {
        if(maxcount < ++hash[s[i] - 'a'])
        {
            maxcount = hash[s[i] - 'a'];
            maxindex = s[i] - 'a';
        }
    }
    
    if(maxcount <= (n + 1) / 2)
    {
        cout << "yes" << endl;
        string ret;
        ret.resize(n);
        
        int index = 0;
        while(maxcount--)
        {
            ret[index] = maxindex + 'a';
            index += 2;
        }
        
        for(int i = 0; i < 26; i++)
        {
            if(hash[i] && i != maxindex)
            {
                while(hash[i]--)
                {
                    if(index >= n)
                    {
                        index = 1;
                    }
                    
                    ret[index] = i + 'a';
                    index += 2;
                }
            }
        }
        
        cout << ret << endl;
    }
    else
    {
        cout << "no" << endl;
    }
    
    return 0;
}

2. Day14

2.1 乒乓球筐(哈希)

  1. 题目链接[编程题]乒乓球筐
  2. 题目描述:

  1. 解法:
    • 算法思路:简单查询题目,可以用哈希表帮助我们解决。
  2. C++ 算法代码:
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string s1, s2;
    while(cin >> s1 >> s2)
    {
        int hash[26] = {0};
        for(auto& ch : s1)
        {
            hash[ch - 'A']++;
        }

        int ret = true;
        for(auto& ch : s2)
        {
            if(hash[ch - 'A'] == 0 || --hash[ch - 'A'] < 0)
            {
                ret = false;
                break;
            }
        }

        cout << (ret ? "Yes" : "No") << endl; 
    }

    return 0;
}

2.2 组队竞赛(贪心)

  1. 题目链接[编程题]组队竞赛
  2. 题目描述:

  1. 解法:
    • 算法思路:小贪心。最高分我们要不到,只能退而求其次拿到倒数第⼆个人的分数。然后⼀直递归这个策略~
  2. C++ 算法代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 1e5 + 10;

int main() 
{
    long long sum = 0;
    int n = 0;
    cin >> n;

    long long arr[N * 3];
    for(int i = 0; i < 3 * n; i++)
    {
        cin >> arr[i];
    }

    sort(arr, arr + 3 * n);
    for(int i = 3 * n - 2; i >= n; i -= 2)
    {
        sum += arr[i];
    }

    cout << sum << endl;

    return 0;
}

2.3 删除相邻数字的最大分数(动态规划 - 线性 dp)

  1. 题目链接DP25 删除相邻数字的最大分数
  2. 题目描述:

  1. 解法:
    • 算法思路:先将各个元素映射到哈希表当中并且将值赋值为自己本身,这样就可以在选择 i 时可以判断 i - 1 和 i + 1的值了。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

const int N = 1e4 + 10;
int sum[N]; // sum[i] 表⽰ i 出现的总和
int n;
int f[N], g[N];

int main() 
{
    int n = 0;
    cin >> n;

    int x = 0;
    for(int i = 0; i < n; i++)
    {
        cin >> x;
        sum[x] += x;
    }

    for(int i = 1; i < N; i++)
    {
        f[i] = g[i - 1] + sum[i];
        g[i] = max(f[i - 1], g[i - 1]);
    }

    cout << max(f[N - 1], g[N - 1]) << endl;

    return 0;
}

3. Day15

3.1 平方数(数学)

  1. 题目链接平方数
  2. 题目描述:

  1. 解法:
    • 算法思路:判断⼀个数开根号之后左右两个数的平方,哪个最近即可。
  2. C++ 算法代码:
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    long long x;
    cin >> x;
    long long a = sqrt(x);
    long long x1 = a * a;
    long long x2 = (a + 1) * (a + 1);
    if(x - x1 < x2 - x)
    {
        cout << x1 << endl;
    }
    else
    {
        cout << x2 << endl;
    }
    
    return 0;
}

3.2 分组(枚举 + 二分)

  1. 题目链接分组
  2. 题目描述:

  1. 解法:
    • 算法思路。
      • 暴力枚举:从小到大枚举所有可能的最⼤值,找到第⼀个符合要求的最大值。
      • 二分优化枚举:二分出符合要求的最大值。
  2. C++ 算法代码:
#include <iostream>
#include <unordered_map>
using namespace std;

bool check(unordered_map<int, int>& cnt, int x, int m)  // 判断最多⼈数为 x 时,能否分成 m 组
{
    int g = 0;
    for(auto& [a, b] : cnt)
    {
        g += b / x + (b % x == 0 ? 0 : 1);
    }
    
    return g <= m;
}

int main()
{
    int n, m;
    cin >> n >> m;
    unordered_map<int, int> cnt; // 统计每种声部的⼈数
    int hmax = 0;
    for(int i = 0; i < n; i++)
    {
        int x = 0;
        cin >> x;
        hmax = max(hmax, ++cnt[x]);
    }
    
    int kinds = cnt.size();
    if(kinds > m)
    {
        cout << -1 << endl;
    }
    else
    {
        // 暴力枚举
//         for(int i = 1; i < hmax; i++)
//         {
//             if(check(cnt, i, m))
//             {
//                 cout << i << endl;
//                 break;
//             }
//         }
        
        // 二分查找
        int left = 1;
        int right = hmax;
        while(left < right)
        {
            int mid = (left + right) / 2;
            if(check(cnt, mid, m))
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        
        cout << left << endl;
    }   
    
    return 0;
}

3.3 【模板】拓扑排序(拓扑排序)

  1. 题目链接AB13 【模板】拓扑排序
  2. 题目描述:

  1. 解法:
    • 算法思路。拓扑排序裸题:
      • 建图;
      • 入队为 0 的点入队;
      • 来⼀次层序遍历即可;
  2. C++ 算法代码:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

const int N = 2e5 + 10;

vector<vector<int>> edges(N); // edges[i] 表⽰ i 这个点所连接的边的信息

int in[N]; // 统计⼊度信息
int n, m;
queue<int> q;
vector<int> ret; // 记录最终结果

int main() 
{
    cin >> n >> m;
    while(m--)
    {
        int a, b;
        cin >> a >> b;
        edges[a].push_back(b);  // 存储边的信息
        in[b]++;    // 存储⼊度
    }

    for(int i = 1; i <= n; i++)  // 把⼊度为 0 的点放进队列中
    {
        if(in[i] == 0)
        {
            q.push(i);
        }
    }

    while(q.size())
    {
        int a = q.front();
        q.pop();
        ret.push_back(a);

        for(auto& b : edges[a])
        {
            if(--in[b] == 0)
            {
                q.push(b);
            }
        }
    }

    // 判断
    if(ret.size() == n)
    {
        for(int i = 0; i < n - 1; i++)
        {
            cout << ret[i] << " ";
        }

        cout << ret[n - 1];
    }
    else 
    {
        cout << -1 << endl;
    }

    
    return 0;
}

4. Day16

4.1 字符串替换(模拟)

  1. 题目链接: QR6 字符串替换
  2. 题目描述:

  1. 解法:
    • 算法思路:简单模拟题~
  2. C++ 算法代码:
class StringFormat {
public:
    string formatString(string A, int n, vector<char> arg, int m) {
        string ret;
        int j = 0;
        for(int i = 0; i < n; i++)
        {
            if(i + 1 < n && A[i] == '%' && A[i + 1] == 's')
            {
                ret += arg[j++];
                i++;
            }
            else 
            {
                ret += A[i];
            }
        }

        if(j < m)
        {
            for(int i = j; j < m; j++)
            {
                ret += arg[i];
            }
        }

        return ret;
    }
};

4.2 神奇数(数学)

  1. 题目链接[编程题]神奇数
  2. 题目描述:

  1. 解法:
    • 算法思路:根据题意模拟就好了,注意⼀些细节问题,比如不要出现前导 0。
  2. C++ 算法代码:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

bool isprim(int n) // 判断是否是质数
{
    if(n < 2)
    {
        return false;
    }

    for(int i = 2; i <= sqrt(n); i++) // 试除法
    {
        if(n % i == 0)
        {
            return false;
        }
    }

    return true;
}

int check(int n)
{
    vector<int> nums;
    while(n)
    {
        nums.push_back(n % 10);
        n /= 10;
    }

    for(int i = 0; i < nums.size(); i++)
    {
        for(int j = 0; j < nums.size(); j++)
        {
            if(i != j && nums[i] != 0)
            {
                if(isprim(nums[i] * 10 + nums[j]))
                {
                    return 1;
                }
            }
        }
    }

    return 0;
}

int main() 
{
    int a, b;
    cin >> a >> b;

    int ret = 0;
    for(int i = a; i <= b; i++)
    {
        ret += check(i);
    }

    cout << ret << endl;

    return 0;    
}

4.3 DNA 序列(滑动窗口)

  1. 题目链接HJ63 DNA序列
  2. 题目描述:

  1. 解法:
    • 算法思路:长度固定的滑动窗口。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

string s;
int n;

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

    int left = 0;
    int right = 0;
    int begin = -1;
    int count = 0;      // 统计窗⼝内 C + G
    int maxcount = 0;   // 存储之前的窗⼝⾥⾯ C + G 的个数
    while(right < s.size()) 
    {
        if(s[right] == 'C' || s[right] == 'G')
        {
            count++;
        }

        if(right - left + 1 > n)
        {
            if(s[left] == 'C' || s[left] == 'G')
            {
                count--;
            }
            
            left++;
        }

        if(right - left + 1 == n)
        {
            if(count > maxcount)
            {
                maxcount = count;
                begin = left;
            }
        }

        right++;
    }

    cout << s.substr(begin, n) << endl;

    return 0;
}

5. Day17

5.1 小乐乐改数字(模拟)

  1. 题目链接: BC45 小乐乐改数字
  2. 题目描述:

  1. 解法:
    • 算法思路。小技巧:题目虽然说输入⼀个数,但是我们可以把它当成字符串读进来,直接操作数字字符串岂不是美滋滋~
  2. C++ 算法代码:
#include <iostream>
using namespace std;

int main() 
{
    string s;
    cin >> s;
    for(long long i = 0; i < s.size(); i++)
    {
        if((s[i] - '0') % 2 == 1)
        {
            s[i] = '1';
        }
        else 
        {
            s[i] = '0';
        }
    }

    cout << stoi(s) << endl;    // ⾃动处理前导零

    return 0;
}

5.2 十字爆破(预处理 + 模拟)

  1. 题目链接: 十字爆破
  2. 题目描述:

  1. 解法:
    • 算法思路:模拟即可,但是由于数据量过大,我们可以提前把每⼀行以及每⼀列的和存起来,方便统计总和。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

const int N = 1e6 + 10;

int main()
{
    long long n, m;
    cin >> n >> m;
    long long row[N], col[N];
    long long arr[n][m];
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            scanf("%ld", &arr[i][j]);
            row[i] += arr[i][j];
            col[j] += arr[i][j];
        }
    }
    
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            printf("%ld ", row[i] + col[j] - arr[i][j]);
        }
        
        printf("\n");
    }
    
    return 0;
}

5.3 比那名居的桃子(前缀和 / 滑动窗口)

  1. 题目链接: 比那名居的桃子
  2. 题目描述:

  1. 解法:
    • 算法思路。滑动窗口的思想:
      • 由题意得,我们是要枚举所有大小为 k 的子数组,并且求出这段子数组中快乐值和羞耻度之和。因此,可以利用滑动窗口的思想,用两个变量维护大小为 k 的窗口的总和,并且不断更新即可。
      • 前缀和思想:这个就比较简单了,先预处理出来快乐值和羞耻度的前缀和数组,然后枚举的过程中直接求出⼀段区间的和即可。但是相比较于滑动窗口的思想,会有空间消耗。
  2. C++ 算法代码:
#include <iostream>
using namespace std;

const int N = 1e5 + 10;

int main()
{
    long long n, k;
    cin >> n >> k;
    long long h[N], s[N];
    for(int i = 0; i < n; i++)
    {
        cin >> h[i];
    }
    
    for(int i = 0; i < n; i++)
    {
        cin >> s[i];
    }
    
    long long left = 0;
    long long right = 0;
    long long hnums = 0;
    long long snums = 0;
    long long begin = 0;
    long long hmax = 0;
    long long smin = 0;
    while(right < n)
    {
        hnums += h[right];
        snums += s[right];
        
        if(right - left + 1 > k)
        {
            hnums -= h[left];
            snums -= s[left];
            left++;
        }
        
        if(right - left + 1 == k)
        {
            if(hnums > hmax)
            {
                begin = left;
                hmax = hnums;
                smin = snums;
            }
            else if(hnums == hmax && smin > snums)
            {
                begin = left;
                smin = snums;
            }
        }
        
        right++;
    }
    
    cout << begin + 1 << endl;
    
    return 0;
}

6. Day18

6.1 压缩字符串(一)(字符串 + 模拟)

  1. 题目链接NC101 压缩字符串(⼀)
  2. 题目描述:

  1. 解法:
    • 算法思路:利用双指针模拟即可。
  2. C++ 算法代码:
#include <string>
class Solution {
public:

    string compressString(string param)
    {
        if(param.empty())
        {
            return "";
        }

        string ret;
        int count = 1;
        for(int i = 0; i < param.size(); i++)
        {
            if(i + 1 < param.size() && param[i] == param[i + 1])
            {
                count++;
            }
            else 
            {
                ret += param[i];
                if(count != 1)
                {
                    ret += to_string(count);
                }

                count = 1;
            }
        }

        return ret;
    }
};

6.2 chika和蜜柑(排序 / topK)

  1. 题目链接chika和蜜柑
  2. 题目描述:

  1. 解法:
    • 算法思路:
      • 排序:将每个橘子按照甜度由高到低排序,相同甜度的橘子按照酸度由低到高排序。
      • 然后提取排序后的前 k 个橘子就好了。
  2. C++ 算法代码:
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 2e5 + 10;

int main()
{
    int n, k;
    cin >> n >> k;
    pair<int, int> arr[N];    // <酸度,甜度>
    for(int i = 0; i < n; i++)
    {
        cin >> arr[i].first;
    }
    
    for(int i = 0; i < n; i++)
    {
        cin >> arr[i].second;
    }
    
    sort(arr, arr + n, [&](const pair<int, int>& a, const pair<int, int>& b)
    {
        if(a.second != b.second)
        {
            return a.second > b.second;
        }
        else
        {
            return a.first < b.first;
        }
    });
    
    long long s = 0;
    long long t = 0;
    for(int i = 0; i < k; i++)
    {
        s += arr[i].first;
        t += arr[i].second;
    }
    
    cout << s << " " << t << endl;
    
    return 0;
}

6.1 01 背包(动态规划 - 01背包)

  1. 题目链接NC145 01背包
  2. 题目描述:

  1. 解法:
    • 算法思路。01 背包模板题:
      1. 状态表示:dp[i][j] 表示从前 i 个物品中挑选,总体积不超过 j 的情况下,最大重量是多少。
      2. 状态转移方程:根据「最后⼀步」的状况,来分情况讨论:
        • 不选第 i 个物品:相当于就是去前 i - 1 个物品中挑选,并且总体积不超过 j 。此时 dp[i][j] = dp[i - 1][j] ;
        • 选择第 i 个物品:那么我就只能去前 i - 1 个物品中,挑选总体积不超过 j - v[i] 的物品。此时 dp[i][j] = dp[i - 1][j - v[i]] + w[i] 。但是这种状态不一定存在,因此需要判断⼀下。
    • 综上,状态转移方程为: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]) 。
  2. C++ 算法代码:
class Solution {
public:
    int dp[1010] = {0};

    int knapsack(int V, int n, vector<vector<int> >& vw) 
    {
        for(int i = 0; i < n; i++)
        {
            for(int j = V; j >= vw[i][0]; j--)
            {
                dp[j] = max(dp[j], dp[j - vw[i][0]] + vw[i][1]);
            }
        }

        return dp[V];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Smile丶凉轩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值