【C++】 —— 笔试刷题day_14

一、乒乓球筐

题目解析

在这里插入图片描述

题目输入两个字符串AB,分别代表AB中的乒乓球,不同的大写字母就表示不同的乒乓球;

如果判断B中的所有乒乓球在A中都有,且A中每种乒乓球的数量大于等于B中的。(简单来说就是BA的子集)。

**对于输入:**有多组输入,<br/>应该是换行;我们就要多次输入(不知道数量)

算法思路

对于这道题,可以说很简单了,我们只需要使用hash表统计B中字符出现的种类和次数,再使用另一个hash表统计A中的种类和数量,如果B中每一种字符出现的次数都相遇等于A中该字符出现的次数,那就输出Yes,否则输出No

这里有一个小小的优化,就是我们比较B中每一个字符出现的次数和A中出现的次数,如果直接比较有些太麻烦了;

**我们可以定义一个count,在将B中字符放入hash2表的同时,记录一下B中字符的种类;**然后了遍历A中字符,可以将A中字符放入hash1,如果A中字符出现的次数等于在B中字符出现的个数了,那就说明这种字符A中是数量是大于B中的,就--count;这样遍历结束后,如果count==0,就说明B中的每一种字符,A中的数量都要大于B中的数量,输出Yes即可,否则输出No

代码实现

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

int main() {
    
    
    string str1, str2;
    while (cin >> str1 >> str2) {
    
    
        int hash1[26] = {
    
    0};
        int hash2[26] = {
    
    0};
        int count = 0;
        for (auto& e : str2) {
    
    
            if (hash2[e - 'A'] == 0)   count++;
            hash2[e - 'A']++;
        }
        for (auto& e : str1) {
    
    
            hash1[e - 'A']++;
            if (hash1[e - 'A'] == hash2[e - 'A'])    count--;
        }
        if (count)   cout << "No" << endl;
        else    cout << "Yes" << endl;
    }
    return 0;
}

二、组队竞赛

题目解析

在这里插入图片描述

它们输入一个n表示有n个队伍,然后紧接着输入3*n个数据,表示每一个选手的水平值;

每一个队伍三个人,然后队伍的水平值是去三个人中水平值第二高的那个。(例如:1,2,5,水平值是`2``)

现在我们要找出来,如何组队让这n个队伍的水平总值最大,然后输出这个最大的水平总值。

算法思路

相信好多人和博主一样,看到这一道题感觉无从下手,感觉有一点点思路但是不多。

现在来看这道题的解法:

我们要找到每一个队伍的水平值,那就要找到第二高的水平值;但是如果整个数据是无序的,我们找起来就非常麻烦;那我们就可以先让数组有序(排序数据

数组有序之后,我们要让这n个队伍的水平总值最大,那我们就要让每一个队伍的水平值尽可能的最大;

这时我们数组有序的作用就体现到了:我们要找每个队伍尽可能大的水平值,那我们就可以从数组最后开始去找,就方便多了。

但是这里,我们队伍的水平值是第二高的,那也就是说,对于整个数据中最大的那一个数它是不能作为任何一个队伍的水平值的,所以n-2位置的水平值可以作为第一个队伍的水平值(此时第一个队伍可以也要包括n-1位置,那第一个队伍就有了n-1、n-2至于第三个队员,我们就要它的水平值尽可能的小,那就是从0位置开始);

对于上面描述,这样分组的好处就是,我们让每一个队伍的水平值都尽可能大,这样我们n个队伍的水平总值肯定是最大的

那这样我们第一个队伍的水平值就是n-2位置的水平值、第二个队伍的水平值就是n-4位置的水平值 …知道第n个位置。

在这里插入图片描述

代码实现

这里数据可能会超出int的范围,我们要使用long long类型来定义最后的结果

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

int main() {
    
    
    int n;
    cin >> n;
    vector<int> v(3 * n);
    for (int i = 0; i < 3 * n; i++) {
    
    
        cin >> v[i];
    }
    sort(v.begin(), v.end());
    long long ret = 0;
    for (int i = 3 * n - 2, count = 1; count <= n; i -= 2, count++) {
    
    
        ret += v[i];
    }
    cout << ret << endl;
    return 0;
}

三、删除相邻数字的最大分数

题目解析

在这里插入图片描述

题目给定n个数据,我们对于这n个数据可以进行一下操作:

删除数组中任意一个元素a,同时数组中a+1a-1的元素会被全部删除,这时我们可以获得a分;

现在我们要计算:我们通过上述操作可以获得的最多的分数。

算法思路

对于这道题,示例一和示例二都没有给存在相同数据,示例三给出了:

在这里插入图片描述

那也就是说,如果存在多个相同的数据a时,我们是可以进行多次移除a的。

那这样我们在进行操作的时候,还要考虑这个元素出现的次数,那我们就可以先统计一下所以数据它出现的个数。

统计玩每一个数据出现的个数,我们再来看如何去求这个得分:

通过读题我们会发现,如果我们不一定要对某一个数据进行操作;

  • 如果我们对一个数据a进行操作了,那我们可能没有对上一个数据a-1进行操作,我们的得分就是没有操作a-1时的得分再加上操作a的得分;
  • 如果我们没有对a进行操作,那我们的得分就要看a-1,此时我们可能对a-1操作了,也可能没有对a-1操作;这时候下我们的得分应该是两种情况中得分最多的。

看到这里,这道题的大致思路已经出来了,那就是动态规划:

在上述分析中,我们发现,我们需要记录两种状态下的数据,一个是对a进行操作的最高得分、还有一个是没有对a进行操作的最高得分;(我们使用dp1来记录对a进行操作的最高得分、dp2来记录对没有对a进行操作的最高得分)。

现在来看状态转移方程,在上述分析中我们也分析了大概了:

  • a进行操作:dp1[a] = dp2[a-1] + sum[a-1],其中sum[i]中记录的是对a进行操作,这一步我们可以获得多少分(在统计每个数据出现的次数时,直接统计对某一个数据操作完可以获得的分数即可)。
  • 不对a进行操作:这时可能对a-1进行操作了,也可能没有对a-1进行操作,此时取两个中最大的即可;dp2[a] = max(dp1[a-1] , dp2[a-1])

在这里插入图片描述

代码实现

这里题目中给定了,数据的范围(1<= a <= 10000),这里我们之间计算到10000即可;

#include <iostream>
using namespace std;

const int N = 10001;
int sum[N];
int dp1[N],dp2[N];

int main()
{
    
    
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
    
    
        int x;
        cin>>x;
        sum[x]+=x;
    }
    //
    dp1[0] = dp2[0] = 0;
    for(int i=1;i<N;i++)
    {
    
    
        dp1[i] = dp2[i-1] + sum[i];
        dp2[i] = max(dp1[i-1],dp2[i-1]);
    }
    cout<<max(dp1[N-1],dp2[N-1])<<endl;
    return 0;
}

到这里本篇文章内容就结束了
感谢各位的支持

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws