C++日常刷题积累
今日刷题汇总 - day023
1、打怪
1.1、题目
1.2、思路
读完题,知道类似于之前经此一役小红所向无敌这道题的回合制模拟打怪的题目,要求进行回合制的攻击,求最后活着的情况下,最多杀几个毛球怪。那么,理解题目和示例,其中如果能杀死无数只,输出-1。所以如果勇士的攻击力恒大于等于毛球怪的血量又是先手,那么一击必杀所以可以击杀无数只毛球怪,则输出-1即可,其次,不能杀无数次的情况,那么退而求其次计算杀一只毛球怪需要几次攻击(回合),然后得到勇士相同回合下会承受几次攻击,那么就能求到击杀一只勇士受到的总攻击遭受损失的血量,从而得到在最大血量下最多能遭受几次损失血量,从而得到最多击杀几只毛球怪。其中,需要注意一些细节问题,计算击杀一只毛球怪时,如果不能整除说明还需要额外攻击一回合才能完成击杀,其次,计算勇士最多能承受几回合血量损失时,要考虑刚好整除杀死一只时的情况,因为题目中提到不接受同归于尽,要求勇士存活的情况下的最多击杀数量。那么接下来,就是程序实现。
1.3、程序实现
首先,按照题目要求写好输入 t 代表测试数据组数,输入四个正整数h,a,H,A,代表你的血量和攻击力以及毛球怪的血量和攻击力,然后根据题目排除特殊情况,如果能杀死无数只,输出-1。
说明a 始终大于等于 H一击必杀,然后计算击杀一只毛球怪需要几回合,同时得到勇士等回合下受到几次攻击,然后就能求到击杀一只时勇士所遭受的血量,最后求得能坚持h/bloodval情况下,最最多的击杀数,注意一些思路分析中的细节处理即可。
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
int h,a,H,A;
while(t--)
{
cin >> h >> a >> H >> A;
//如果能杀死无数只,输出-1。
if(a >= H)
cout << -1 << endl;
else
{
//一只毛球怪抗几次伤害
int m = H/a + (H%a == 0 ? 0 : 1);
//勇士等比回合下,抗几次伤害(先手)
int n = m-1;
//杀死⼀只毛球怪的时候,勇士会掉多少⾎
int bloodval = n*A;
//能够杀几只,且要活着
int ret = h/bloodval - (h%bloodval == 0 ? 1 : 0);
cout << ret << endl;
}
}
return 0;
}
2、字符串分类
2.1、题目
2.2、思路
读完题,知道要求对N个字符串进行分类,分类满足A串中交换任意位置的两个字符,最终可以得到B串,交换的次数不限,求最后能分为几类。根据示例和题目分析,最开始想着利用ASCII码的和相同进行区分分类,又想了一下,题目中并没有规定字符串必须等长,所以继续思考,发现既然是同一类字符串,那么它们的字符和顺序相同归为一组,那么采用之前解决集合的思路采用set这个关联式容器,解决的去重和排序问题。
底层本质:C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
而这里针对字符串,也可以采用unordered_set。
unordered_set 是 C++ 标准模板库(STL)中的一个关联容器,它存储的元素不重复,且元素的顺序是未定义的(即无序的)。unordered_set 内部通常实现为哈希表,因此它能够提供平均常数时间复杂度的查找、插入和删除操作。
2.2.1、unordered_set和set的区别
特性 | unordered_set | set |
---|---|---|
元素顺序 | 无序。元素的顺序由哈希函数计算得出,每次插入或删除操作后,元素的顺序都可能改变。 | 有序。元素根据键值自动排序(默认为升序),保持元素的相对顺序不变。 |
实现机制 | 使用哈希表实现。通过哈希函数将元素映射到特定的桶中,以实现快速的插入、查找和删除操作。 | 使用红黑树(一种自平衡二叉查找树)实现。这使得元素按照键值有序存储,并支持稳定的对数时间复杂度的查找操作。 |
查找时间复杂度 | 平均情况下为常数时间复杂度O(1),但在最坏情况下可能退化到线性时间复杂度O(n),这取决于哈希函数的质量和元素的分布情况。 | 对数时间复杂度O(log n),因为红黑树是一种平衡树,能够保证查找、插入和删除操作的时间复杂度均为对数级别。 |
迭代器稳定性 | 迭代器在元素插入后可能会失效,因为哈希表可能会重新分配桶,导致元素在内存中的位置发生变化。 | 迭代器在元素插入和删除操作后仍然有效,因为红黑树在修改操作时能够保持节点的相对位置不变。 |
性能 | 通常比set在插入、查找和删除操作上有更快的性能,尤其是在元素数量较大时。但哈希表可能会导致额外的空间消耗和冲突处理开销。 | 性能稳定,但相对于unordered_set,在插入、查找和删除操作上可能稍慢一些。 |
接下来,直接上两组代码即可。
2.3、程序实现 – set
set
#include <iostream>
#include <algorithm>
#include <string>
#include <set>
using namespace std;
int main()
{
int N;
cin >> N;
string str;
set<string> hash;
for(int i = 0;i < N;i++)
{
cin >> str;
sort(str.begin(),str.end());
hash.insert(str);
}
cout << hash.size() << endl;
return 0;
}
unordered_set
#include <iostream>
#include <algorithm>
#include <string>
#include <unordered_set>
using namespace std;
int main()
{
int N;
cin >> N;
string str;
unordered_set<string> hash;
for(int i = 0;i < N;i++)
{
cin >> str;
sort(str.begin(),str.end());
hash.insert(str);
}
cout << hash.size() << endl;
return 0;
}
3、城市群数量
3.1、题目
3.2、思路
读完题,知道属于搜索类题型,要求在一个 n 个节点的邻接矩阵 m中,存在多少个城市群,其中节点定义为城市,如果 a 城市与 b 城市相连, b 与 c 城市相连,尽管 a 与 c 并不直接相连,但可以认为 a 与 c 相连,定义 a,b,c 是一个城市群。那么,可以⽤ dfs 或者 bfs 解决,这里就使用dfs思路即可。那么为了方便理解画个图:
所以实际涉及的知识点有,图论、连通块、bfs、dfs、flood fill算法等知识,那么,这里采用dfs利用hash思想对搜索过的城市进行标记,遍历这个连通图,如果遍历了就进行标记,同时ret统计连通块的数量,然后继续依次遍历直到遍历结束即可。为了直观的理解画个图:
先遍历第一个图ret++,标记0,2,4,然后遍历完后,继续遍历第二个图ret++,标记1,然后继续遍历2,由于2已经被标记不用再统计,越过去遍历3,3未被标记执行遍历第三个图ret++,标记3,5,直到遍历结束,ret = 3即可。
接下来,就是程序实现。
3.3、程序实现 – dfs
class Solution {
public:
bool vis[210] = { 0 }; // ⽤来标记当前位置是否已经搜索过
int citys(vector<vector<int> >& m)
{
int n = m.size();
int ret = 0;
for(int i = 0;i < n;i++)
{
if(!vis[i])
{
ret++;
//dfs标记连通块
dfs(m,i);
}
}
return ret;
}
void dfs(vector<vector<int> >& m, int pos)
{
vis[pos] = true;
int n = m.size();
for(int i = 0;i< n;i++)
{
if(!vis[i] && m[pos][i])//m[pos][i]表示从pos位置到i位置存在边,即是连通的
{
dfs(m,i);//继续标记
}
}
}
};