【笔试强训day14】

目录

第一题:乒乓球筐

输入描述:

输出描述:

输入

输出

第二题:组队竞赛 

输入描述:

输出描述:

输入

输出

第三题:删除相邻数字的最大分数

描述

输入描述:

输出描述:


第一题:乒乓球筐

题目链接:乒乓球筐__牛客网

nowcoder有两盒(A、B)乒乓球,有红双喜的、有亚力亚的……现在他需要判别A盒是否包含了B盒中所有的种类,并且每种球的数量不少于B盒中的数量,该怎么办呢?

输入描述:

输入有多组数据。
每组数据包含两个字符串A、B,代表A盒与B盒中的乒乓球,每个乒乓球用一个大写字母表示,即相同类型的乒乓球为相同的大写字母。
字符串长度不大于10000。

输出描述:

每一组输入对应一行输出:如果B盒中所有球的类型在A中都有,并且每种球的数量都不大于A,则输出“Yes”;否则输出“No”。

示例1

输入

ABCDFYE CDE

ABCDGEAS CDECDE

输出

Yes

No

 题意概括起来就是判断B是否是A的子集,如果是,输出Yes;否则输出No。

解法一:用两个hash数组来统计两个字符串中每个字符出现的次数,然后遍历字符串B,只要B中所有出现的字符的个数都小于等于A中对应字符的次数,B就是A的子集。反之,则不是。

解法二:用一个hash数组来统计A中各个字符出现的次数,然后遍历B,通过B中字符找到该字符在hash数组中的位置,然后减一即可。如果hash数组中有某个位置被减到小于0,那么说明B中某个字符出现的次数大于A中出现的次数或者是B中有的字符A中没有,输出No;反之输出Yes。

这两种解法都可以解决问题,但是为了减少一个空间上的开销,下面我采用解法二。

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

int main() 
{
    string a, b;
    while(cin >> a >> b)
    {
        int hash[26] = { 0 };
        for(auto ch : a) hash[ch - 'A']++;
        bool flag = true;
        for(auto ch : b)
        {
            if(--hash[ch - 'A'] < 0)
            {
                flag = false;
                break;
            }
        }
        if(flag) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
}

第二题:组队竞赛 

 题目链接:组队竞赛_牛客笔试题_牛客网

牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。
例如:
一个队伍三个队员的水平值分别是3,3,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是3,2,3.那么队伍的水平值是3
一个队伍三个队员的水平值分别是1,5,2.那么队伍的水平值是2
为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。
如样例所示:
如果牛牛把6个队员划分到两个队伍
如果方案为:
team1:{1,2,5}, team2:{5,5,8}, 这时候水平值总和为7.
而如果方案为:
team1:{2,5,8}, team2:{1,5,5}, 这时候水平值总和为10.
没有比总和为10更大的方案,所以输出10.

输入描述:

输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)

第二行包括3*n个整数a_i(1 ≤ a_i ≤ 10^9),表示每个参赛选手的水平值.

输出描述:

输出一个整数表示所有队伍的水平值总和最大值.

示例1

输入

2
5 2 8 5 1 5

输出

10

排序后,我们很想其中一组的水平值为8,但是根本做不到。只能退而求其次,只能取第二大的数,要取第二大的数5,就得顺带把最大的8放到同一组,不然它又是最大的。

 

此时第一组还要在填入一个数, 我们不可能把5填进去,因为会白白浪费掉一个大的数,导致总和不是最大的。所以我们会把1放进去。第一组就为 1   5    8.水平值为5.

 

同理,第二组也是同样的策略。把剩余的最大的数和次大的数放到第二组,然后在把剩下的数中最小的也加入第二组,进而得到第二组为 2 5 5.水平值为5. 

圈起来的数就是我们取到的最大的水平值。规律就是:排序后,从倒数第二个数开始取,取完倒数第二个数后,往前隔一个位置再取,直到取出n个。把取出的这些数加起来就是答案。

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

const int N = 1e5 + 10;
int arr[N *3];

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

    sort(arr, arr + 3 * n);//排序

    long long ret = 0;//int会溢出
    int pos = 3 * n - 2;//倒数第二个位置
    while(n--)
    {
        ret += arr[pos];
        pos -= 2;//隔一个在取
    }
    cout << ret << endl;
    return 0;
}

第三题:删除相邻数字的最大分数

题目链接: 删除相邻数字的最大分数_牛客题霸_牛客网

描述

给定一个长度为 n 的仅包含正整数的数组,另外有一些操作,每次操作你可以选择数组中的任意一个元素 𝑎𝑖  ,同时数组中所有等于 𝑎𝑖−1   和 𝑎𝑖+1  的元素会被全部移除,同时你可以得到 𝑎𝑖   分,直到所有的元素都被选择或者删除。
请你计算最多能得到多少分。
数据范围: 数组长度满足 1≤𝑛≤10^5 1≤n≤10^5  ,数组中的元素大小都满足 1≤𝑎𝑖≤10^4. 

输入描述:

第一行输入一个正整数 n 表示数组的长度

第二行输入 n 个数字表示数组的各个元素值。

输出描述:

输出能得到的最大分数。

示例1

输入:

9
1 2 1 3 2 2 2 2 3

输出:

10

说明:第一步选择一个 2 ,然后所有 1 和 3 都被移除了,此时数组中剩下的是 [2,2,2,2] ,依次选择他们即可得到 10 分

通过对数组进行预处理,然后转化为另个一问题。

预处理用做的就是:统计数组中每个数字的总和,存放进一个hash数组中,由题意,选中一个数以后,该数+1和-1的数就不能再选,在hash数组中的表现就是,选中一个数后,它的相邻位置就不能再选了(类似于打家劫舍问题,不知道也没关系)。所以可以用动态规划来解决这个问题。

动态规划五部曲:

1)状态表示:

对于hash数组中的某一个位置,可选可不选,因此状态表示有两种。

f [ i ]表示选该位置得到的最大分数。g[ i ]表示不选该位置的得到的最大分数。

2)推导状态转移方程:

f[ i ] = hash[ i ] + g[ i - 1]        g[ i ] = max(f [ i  - 1],g[ i - 1])

如果选择 i 位置的值,那么前面i - 1位置就不能选,就是f [ i ] 的最大值就是i对应的和+不选i - 1位置的最大分数,而不选 i - 1的最大分数就是存在g[i - 1]中,故得到f[i]的状态表示。

如果不选 i 位置,那么前面的 i - 1位置有两种情况,可以选 i - 1位置,也可以不选 i - 1位置,但是g[ i ]表示不选 i 位置的最大分数,所以g[ i ]就取选和不选这两种情况种的最大值。 

3)初始化

从1开始填,因为都是正整数,没有0,所以f[0]和g[0]都为0。

4)填表顺序

因为后面的状态会依赖于前面的,所以从左往右填。

5)确定返回值

返回值就是max(f[N - 1],g[N - 1])。这样可以确保所有数都被选择或者被删除过了。

#include <iostream>
using namespace std;

const int N = 1e4 + 10;
int sum[N] = { 0 };//hash数组
int f[N], g[N];    //dp数组

int main() 
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        int x;
        cin >> x;
        sum[x] += x;//统计数字的总和
    }
    for(int i = 1; i < N; i++)
    {
        //填表
        f[i] = sum[i] + g[i - 1];
        g[i] = max(f[i - 1], g[i - 1]);
    }
    cout << max(f[N - 1], g[N - 1]) << endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值