今天做了几道ABC题,发现Codeforces上的简单题看上去都很唬人但实际上要做的事情非常简单,只要能从题目里找到真正要做的东西
Codeforces Round 957 (Div. 3)A. Only Pluses B. Angry Monk C. Gorilla and Permutation
先来看A题:
题目大意是输入A,B,C,你可以选择一个数+1并重复此操作五次,输出重复操作后最大的A*B*C的值
首先想到的是贪心,每次+1操作时看看每种情况增加的值,然后选择最大的那种情况
但是稍微想想就能发现每次在最小的那个数上+1就可以了(乘法分配律,把+1拆出来)
#include<iostream>
using namespace std;
int min(int arr[3])
{
int min = arr[0];
int result = 0;
for (int i = 0; i < 3; i++)
{
if (arr[i] < min)
{
min = arr[i];
result = i;
}
}
return result;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int arr[3]={0,0,0};
for (int i = 0; i < 3; i++)
{
cin >> arr[i];
}
for (int i = 0; i < 5; i++)
{
arr[min(arr)] += 1;
}
cout << arr[0] * arr[1] * arr[2] << endl;
}
return 0;
}
B题:
题目大意:输入一个数组,你可以对它进行两种操作:
1.选择一个元素并将它拆为a-1,1再放入数组中(数组大小会+1)
2.选择数组中的一个元素和一个1,将它们合并(数组大小会-1)
问最少经过几步操作可以让数组大小变成1
乍一看好像有点难以想到一个可以最小次数的方法
但是仔细想想就会发现其实这两个操作是很固定的,要想数组大小为1,只有把所有数都变成1再结合,那么最小次数的方法就是选择一个最大的数不拆,然后把其他的数全部拆成1在跟最大的数结合即可,每个数n都要经过n-1次拆和n次合并(要注意1不需要拆只要1次合并)
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int m, n;
cin >> n >> m;
vector<int> arr(m);
for (int i = 0; i < m; i++)
{
cin >> arr[i];
}
vector<int>::iterator p1 = arr.begin();
int max = 0;
vector<int>::iterator p2 = arr.begin();
while (p1 != arr.end())
{
if (*p1 > max)
{
max = *p1;
p2 = p1;
}
p1++;
}
vector<int>::iterator p3 = arr.begin();
int result = 0;
while (p3 != arr.end())
{
if (p3 == p2)
{
p3++;
continue;
}
else if (*p3 == 1)
result += 1;
else
{
result += (*p3) * 2 - 1;
}
p3++;
}
cout << result << endl;
}
}
最后是C题:
题目大意:定义两个函数g(i)和f(i),g(i)是数组前i个数中不大于m的数的和,f(i)是数组前i个数中不小于k的数的和,现在给你一个大小为n的数组(数组的元素是1~n,且没有重复),让你把这个数组的顺序变化,使得最大
又是看上去很复杂但其理解了之后很简单的题目,只需要把大的数往前挪,小的数往后挪即可(但是最后m个数要升序排列,因为g(i)在计算时(假设时不大于2的数),{1,2,3}的计算结果是1+(1+2)+(1+2),但是{3,2,1}的计算结果是0+2+(1+2),{3,1,2}的计算结果是0+1+(1+2),我们要让小的数尽可能被计算多次,大的数尽可能被计算的少)
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int m, n, k;
cin >> n >> m >> k;
vector<int> arr(n);
for (int i = n-m; i <n ; i++)
{
arr[i] = m-n+i+1;
}
for (int i =0; i < n - m; i++)
{
arr[i] = n - i;
}
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
return 0;
}
最后再附加一个A题,也是非常简单但是看上去很唬人的类型:
题目大意:有一个接硬币游戏,有许多硬币在坐标系中,而你在(0,0)点,你每秒可以向八个方向移动,也就是你可以对你的坐标做以下的操作
同时你每次移动后,硬币就会往下落(即硬币纵坐标-1),要判断你能否接到这个硬币
看上去好像很复杂,你需要计算你跟硬币之间的距离,还要考虑硬币的下落后变化的坐标
但其实只需要考虑硬币的纵坐标即可,因为你可以对你的横坐标纵坐标同时-1,所以你是可以在保持你和硬币的纵坐标差值不变的情况下让自己和硬币的横坐标差为0,所以你只需要考虑自己能否做到让自己和硬币的纵坐标差>=0即可(如果硬币在你的下面那你就永远无法接到了,因为你-1,硬币也会-1),所以只要判断硬币的纵坐标+1(+1是因为你是先动的那个)是否大于你的纵坐标(也就是0)就可以判断结果了
#include<iostream>
using namespace std;
int main()
{
int m;
cin >> m;
while (m--)
{
pair<int, int> pos;
cin >> pos.first >> pos.second;
if (pos.second + 1>=0)
{
cout << "YES" << endl;
}
else
cout << "NO" << endl;
}
return 0;
}
这道题我刚看完想到的是去模拟这个过程,优先选择同时+1,-1的操作尽快接近然后在看能不能接到,但仔细想想发现完全没有这么麻烦