Coding Practice,48天强训(14)

Topic 1:乒乓球筐(哈希)

map秒了,这里需要注意的一个问题就是最后的判断循环里,为什么i能直接=‘A’,其实使用了它的ASCII码值,等价于  for (int i = 65; i <= 90; i++),在之后的比较中隐式类型转换了

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

int main() 
{
    string a, b;
    // cin >>做条件,持续读取输入,直到没有更多输入为止
    while (cin >> a >> b) 
    {
        // A记录字符串a中各字符的出现次数,B记录字符串b中各字符的出现次数
        map<char, int> A, B;
        for (int i = 0; i < a.size(); i++) // 遍历字符串a,统计每个字符出现的次数
        { 
            A[a[i]]++;  // 哈希中对应字符的计数加1
        }
        for (int i = 0; i < b.size(); i++) //同上处理b
        { 
            B[b[i]]++;
        }

        for (int i = 'A'; i <= 'Z'; i++) 
        {
            if (B[i] > A[i]) // 如果字符串b中某字母的出现次数多于字符串a中的
            {
                cout << "No" << endl;// 不符条件
                break;
            }
            if (i == 'Z') // 如果已经检查到最后一个字母'Z'且没有发现问题
            {
                cout << "Yes" << endl;  //b可以由a的字符组成,输出
            }
        }
    }
    return 0;
}

优化一下

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

bool canConstruct(const string& a, const string& b) 
{
    if (b.size() > a.size()) return false;// 健壮
    
    vector<int> v(26, 0);// 字母数量少,可以直接用vector提高效率
    for (const auto& c : a) 
    {
        v[c - 'A']++;// 字符-'A'得到0-25的ASCII整数值,映射到v里就代表对应字母的数量++
    }
    for (const auto& c : b) 
    {
        if (--v[c - 'A'] < 0) // 如果b中对应字符数量小于a,不符合条件
        {
            return false;// 不匹配
        }
    }
    return true;// 一套下来都没问题,匹配
}

int main() 
{
    ios::sync_with_stdio(false);// 增加输入出效率
    cin.tie(nullptr);
    
    string a, b;
    while (cin >> a >> b) 
    {
        cout << (canConstruct(a, b) ? "Yes\n" : "No\n");
    }
    return 0;
}

Topic 2:组队竞赛(贪心)

三数取一,取每组的中间数相加,最后得到一个最大的结果,那么让所有组局部最优,最后构成全局最优,由于是取中间数,所以每组的局部最优是(最小,次大,最大)这样的结构,用指针来表示就是在一个升序序列中,最小指针指向最左元素,最大指针指向最右元素,次大指向右数第二个元素,像这样不断往中间遍历构成小组,即可实现局部分数最优,最后相加形成全局最优。

#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());
    int l = 0, r = 3 * n - 2;
    long long res = 0;
    while(l < r)
    {
        res += v[r];
        l++;
        r -= 2;
    }
    cout << res;

    return 0;
}

优化优化,最后的循环可以写个公式

#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 res = 0;
    // 从倒数第2个开始(中间数就是),一组取一个,共取n个,
    for (int i = 0; i < n; ++i) 
    {
        res += v[3 * n - 2 * (i + 1)];
    }
    cout << res << endl;
    
    return 0;
}

Topic3:删除相邻数字的最大分数(动态规划,打家劫舍)

leetcode 089 打家劫舍 类似,稍稍变体,如果把题中数组处理一下,就是一个打家劫舍问题,比如 1 2 1 3 2 2 2 2 3 这个数组,我们稍微处理一下,把它做成哈希

根据题意,每次处理会消耗掉左右两边所有的数字,比如选2,那么1 3就都没了,选5,那么4 6 就都没了,所以当做成下面哈希的形式,里面存上对应数字在数组中的总分值(总数*单个分值),那么这不就是个打家劫舍的问题吗,左右两边就相当于不可以再次进入的房间,那么只要求出哈希中能够找到的最大价值,用动态规划的方法,这题就解决了。

那么给、现在搞定递推公式,以上面的数组为例,我改进一下例子

dp[i]就是——包含下标i的情况(可能拿这个下标对应的价值,也可能不拿)下,能得到的最大价值;所以最大价值也就会在我们求dp[哈希数组的元素个数 - 1](元素6的位置)(456元素原题是没有的,是为了方便理解我才加进去的;)时得出,那么我们要求6位置的价值,5肯定不能被考虑,因为相邻左右的元素会被删除嘛,也就是打家劫舍中相邻的房间不可以偷,会触发警报,原理是一样的,所以当我们选择收取6位置的价值,我们就会获得dp[i - 2] + hs[i],如果我们不收取6位置的价值,也就是选择可以收取5位置价值的那条路线,我们会考虑要不要收取5位置的价值,也就是dp[i - 1],所以最终

dp[i] = max(dp[i - 2] + hs[i], dp[i - 1])

这就是递推公式,接下来搞搞初始化,那么根据条件我们需要求dp[0] 和dp[1]两个位置,我们该如何初始化呢?dp[0],当考虑下标0位置的最大价值,就一个房间,肯定要拿,所以dp[0] = hs[0];

dp[1]呢?dp[1] = max(hs[0],hs[1]),两个房间里拿多的那个呗,所以带入dp[i]中,从小到大递推,最后得出结果反应到对应的元素即可

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

int main() 
{
    int n;
    cin >> n;
    vector<int> v(n);
    long long maxnum = 0;
    for (int i = 0; i < n; ++i) 
    {
        cin >> v[i];
        if (v[i] > maxnum) 
        {
            maxnum = v[i];// 记录最大值待会儿开空间用,当然也能直接用哈希表
        }
    }
    vector<long long> hs(maxnum + 2, 0);// 多开2个避免从0 1开始计算的溢出
    for (const int& e : v) 
    {
        hs[e] += e;// 在对应元素位置累加价值
    }
    vector<long long> dp(maxnum + 2, 0);
    dp[0] = hs[0];
    dp[1] = max(hs[0], hs[1]);

    for (int i = 2; i <= maxnum; ++i) 
    {
        dp[i] = max(dp[i - 2] + hs[i], dp[i - 1]);
    }

    cout << dp[maxnum];
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值