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;
}