暑假算法集训(第二周)
开始算法集训的第二周,我明显感受到了疲感,可能是内心中的冲动的减弱,题目越来越难,做题时的状态明显不如上周。既然选择了要试试,就不要草草了事,加油啊!
题目回顾
一、Spell Check CodeForces - 1722A
题目解析:
这道题是要让我们输入一个目标字符串,后来在输入待定字符串,如果字符串是目标字符串的排序则输出yes,否则输出no。这道题主要是要如何处理好字符的排序的问题。这里有两个思路,一个是将字符输入到set中,因为set容器自身保持有序的属性可以使输入的如目标字符都进行排序,就不用考虑问题。第二是用STL中的next_permutation保存目标字符的所有排序序列,在于输入比较,但如果目标字符过长,则全排的种数过大,会使代码效率过低。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
set<char> target = { 'T', 'i', 'm', 'u', 'r' };//设置字母目标集合
int t;cin >> t;//设置输入案例数量
while (t--) {
int n;cin >> n; //设置输入的字符数
string s;cin >> s;//设置输入字符串
if (n != 5) {//如果输入字符数不是目标数可以直接输出no
cout << "NO" << endl;
continue;
}
set<char> charSet(s.begin(), s.end());//定义输入的字符集合
if (charSet == target) {//如果目标集合和输入字符集合相同输出yes,否则输出no
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
}
return 0;
}
Array Cloning Technique CodeForces - 1665B
题目解析:
注意map的使用,有点类似于python中的字典,可以通过第一个键去获取它对应的值,这里可以用它来统计输入的不同字符的字数,因为用已出现的字母次数大的赋值操作数可以越低。这道题读题理解太慢了,一开始花了很长时间去理解题意。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, t;long long d;
cin >> n;//设置输入组数
while (n--) {
cin >> t;//定义输入数组长度
int Max = 0;
map<long long, int> m;
for (int i = 0; i < t; i++) {
cin >> d;//输入数字
m[d]++;//输入的数组的出现字数加一
Max = max(m[d], Max);//选择输入中出现的最大值进行复制
}
int ans = 0;//初始化赋值次数
while (Max < t) {//当最长的数组数已经超过输入长度则结束
ans++;//默认加一
if (2 * Max <= t) {//输入可以复制两倍的话加一倍操作数
ans += Max;
Max *= 2;
} else {
ans += t - Max;
Max = t;
}
}
cout << ans << endl;
}
return 0;
}
*Number Sequence *
题目解析:
这道题主要是去寻找循环节,因为总体取模等于分别取模再取模,那么第一项有7种可能,第二项也有7种可能所以总共对应取模最后最多49次后就会进入循环。
AC代码:
#include<stdio.h>
int main(){
int a,b,n;
while(scanf("%d%d%d",&a,&b,&n)!=EOF){//输入变量
if(a==0&&b==0&&n==0) break;//当变量都输入0时
int c[50]={0};
c[0]=1;c[1]=1; //数组从第二项开始
for(int i=2;i<49;i++)
c[i] = (a*c[i-1]+b*c[i-2])%7;//按题意取模
printf("%d\n",c[n%49-1]);
}
return 0;
}
The 3n + 1 problem
题目解析:
这道题其实只要按照题目给的步骤进行编程就行,但是注意大坑,i和j没有定谁大。在这wa了,一开始还没有反应过来,一直在检查代码和在读题,思维还是不够严谨。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int problem(int n) {
int count = 1;
while (true) {
if (n == 1)break;
if (n % 2 == 1)n = 3 * n + 1;
else n = n / 2;
count++;
}
return count ;
}
signed main() {
int i, j;
while (cin >> i >> j) {
cout << i << " " << j << " ";
if (i > j)swap(i, j);
int max1 = problem(i);
for (int k = i+1; k <= j; k++) {
if (problem(k) > max1)max1 = problem(k);
}
cout << max1 << endl;
}
return 0;
}
AK比赛!
题目解析:
这道题难点在于怎么把给的ak比赛规则转译成代码公式,这里的罚时是过的时间加上wa的次数*20,这里的过得时间要加上之前的过得时间加上这道题的编写时间和修改代码的时间。一开始没有完全理解完代码就开始编写了,导致在中间公式的编写出现了偏差,代码完成的时间没有边上之前修改的时间,还是不太细致啊。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
long long n, k; cin >> n >> k;
long long t = 0; long long at=0;
while (n--) {
long long m, n; cin >> m >> n;
at +=(t +m+ n*k) +(20*n);//注意完成时间是累加的。
t += m + n * k;
}
cout << at ;
return 0;
}
Minimum Glutton
题目解析:
这道题是分别吃甜咸两系列的采,看最少吃哪一系列可以最少超过限制。其实这里可以不用按我的代码写,一开始我以为map可以默认进行按值进行排序,后复制元素进入vector在排序,其实可以一开始直接存进vector中,是对知识点把握度的不足。后面补充了相关知识信息。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int, int> sw;
map<int, int> sa;
int main() {
int n; ll x, y;
cin >> n >> x >> y;
for (int i = 1; i <= n; i++) {
int x1; cin >> x1;
sw[i] = x1;
}
for (int i = 1; i <= n; i++) {
int x2; cin >> x2;
sa[i] = x2;
}
vector<pair<int, int>> sw_vec(sw.begin(), sw.end());
vector<pair<int, int>> sa_vec(sa.begin(), sa.end());
auto cmp = [](pair<int, int>& a, pair<int, int>& b) {
return a.second > b.second;
};
sort(sw_vec.begin(), sw_vec.end(), cmp);
sort(sa_vec.begin(), sa_vec.end(), cmp);
int min_count = 0;
int count = 0;
ll sum = 0;
// 计算sw中满足sum <= x的最小数量
for (auto i : sw_vec) {
if (sum > x) break;
sum += i.second;
count++;
}
min_count = count;
sum = 0;
count = 0;
// 计算sa中满足sum <= x的最小数量
for (auto i : sa_vec) {
if (sum > y) break;
sum += i.second;
count++;
}
min_count = min(min_count, count);
cout << min_count;
return 0;
}
知识点回顾
容器映射(map/multimap)
是一种基于键值存放的容器,map为一一对应,而multimap允许重复的键值。set的键值都为Key形式,而map的键值为一个pair的两个分量。
map的内部包含一颗红黑树(严格意义上是一颗平衡二叉树)具有自动排序功能
-
包含头文件:
#include<map>
-
构造函数
-
- map c //产生一个空的map
- map c(op)//产生一个基于op准则排序的空map
- map c1(c2) //产生c2的副本,所有元素都被复制
- map c(beg,end) //以取[beg,end]内的元素产生一个map
- map c(beg,end,op) //以op为排序准则,取区间内的元素产生一个map
- c.~map()销毁map,释放内存
-
map可以选择的形式
-
map<keytype,elem>
//一个map,以less<>(operator)为排序准则map<keytype,elem,op>//一个map,以op准则排序
multimap<keytype,elem>
//一个map,以less<>(operator)为排序准则multimap<keytype,elem,op>//一个map,以op准则排序
-
成员函数
-
-
iterator begin(), end() //指向第一和最后的一个迭代器
-
void clear() //清空容器
-
bool empty() //如果为空返回true,否则为false
-
insert(pair<keytype,valuetype>,&elem)
//插入一个pair类型的元素,返回插入元素的位置 -
void insert(iterator start ,iterator end)//插入[start,end)之间的元素
-
void erase(iterator loc)//删除loc所值的元素
-
void erase(iterator start,iterator end)//删除[start,end)范围内的元素
-
size_type erase(const keytype &key)//删除key为value的值,并返回删的个数
-
iterator find(const key_type&key) //返回一个迭代器指向键值为key的元素,没找到就返回end()
-
size_type count() //返回键值等于key的元素个数
-
size_type size() //返回元素的数量
-
void swap() //交换两个map中的元素
keytype为键值的类型,elem为value值的类型
-
-
元素的访问
- 定义迭代器:
map<string,float>::iterator pos;
- 定义迭代器:
-
具体使用
map<string,float,less<string> >c;//防止把>>看成重载符,中间留个空格
//模版类构造函数
c.insert(make_pair("cake",7.75));//模版函数形成pair对象
c.insert(make_pair("banaba",1.72));
c.insert(make_pair("pizza",1.80));
c[wine]=15.66;//想数组一样构造
map<string,float>::iterator pos;
for(pos=c.begin();pos!=c.end();pos++)
cout<<pos->first<<" "<<pos->second<<endl;
c.clear();
容器列表
他的数据是由若干个节点,每个节点包括当前节点的值和一个前驱节点和后继节点。它不需分配指定的内存大小且可以任意伸缩,这是因为他存储在分连续的内存空间内,并通过指针将有序的元素连接起来。
便于修改不便于检索
-
头文件:
#Include<list>
-
构造函数:
- list(TYPE) c //产生一个空列表
- list(TYPE)c1(c2) //产生一个与c2相同的列表
- list(TYPE) c(n,type) //产生n个type的列表
- list(TYPE)c(beg,end) //产生一个list,并以一个[beg,end)内的元素为初值
-
成员函数
集合容器(set)
包含唯一元素。集合中的元素按照一定的顺序排序。对于集合这个序列可以进行查找,插入,删除其中任意一个元素。
集合通过一个链表来组织,其具体实现采用红黑树的平衡二叉树的数据结构。
-
头文件
#include<set>
-
构造函数:
- set c 产生一个空的集合
- set c(op) //以op为排序准则产生一个空的集合