round34
B.
题目内容:小红拿到了一个数组,她修改尽可能少的元素使其变成 非排列 。你能帮帮她吗?
定义排列为一个长度为n的数组,其中1到n每个元素恰好出现一次。
自己做的时候费了好大劲儿,主要是没有写成时间复杂度较低的判排列代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, i = 0, temp = 0;
cin >> n;
vector<int> a(n + 1, 0);
for( ; i < n; i++) {
cin >> temp;
if(temp <= n) {
a[temp]++;
}else{
cout << 0;
return 0;
}
}
for(i = 1; i <= n; i++) {
if(a[i] != 1) {
cout << 0;
return 0;
}
}
cout << 1 << '\n' << "1 1000000000";
return 0;
}
判断一个数在一串数字里有没有出现且是否只出现了一次,可以用一个记录数组a,每输入一次就将对应值加1,最后循环遍历判断即可
unorder_set法(新学的):
std::unordered_set
用于存储唯一的元素集合。它提供了快速的插入、查找和删除操作,平均时间复杂度为 O(1)。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0;
cin >> n;
unordered_set<int> a;
int temp = 0;
for(int i = 0; i < n; i++) {
cin >> temp;
a.insert(temp);
}
for(int i = 1; i <= n; i++) {
if(a.find(i) == a.end()) {
cout << 0;
return 0;
}
}
cout << 1 << '\n' << "1 1000000000";
return 0;
}
C.
题目内容:小红拿到了一个偶数,她希望你将其切割成尽可能多的偶数。你能帮帮她吗?
输出若干行,从小到大输出每个偶数。
考点是贪心,思路是遇到偶数就切割,遇到奇数就继续往下走直到遇到偶数了再切割,由于输入的偶数范围很大,超出了各种整数能表示的范围,因此使用string。但是使用string进行升序排列时会按照第一个字符的字典序,即“12<3”,因此需要在sort函数里重载一个自定义排序函数(今天新学的)。
自己做的时候吧,切割的思路倒是对的,但是代码实现还是没过关(语言基础真的好差呜呜呜)
#include <bits/stdc++.h>
using namespace std;
bool cmp(string a, string b)
{
if(a.length() == b.length()) return a < b;
else return a.length() < b.length();
}
int main()
{
string s;
cin >> s;
vector<string> a;
string temp;//将s切割成一个个temp字符串,压入a里进行大小比较
for(auto i : s) {
temp += i;
if((i - '0') % 2 == 0) {
a.push_back(temp);
temp = "";
}
}
sort(a.begin(), a.end(), cmp);
for(auto v : a) {
cout << v << '\n';
}
return 0;
}
temp里压入偶数就直接切割放入a容器里,遇到奇数就继续往下遍历(妙啊,我当时写了坨什么勾事),然后sort函数里重载了自定义的cmp函数,接受两个字符串,如果两个字符串长度相等,那么返回他们的字典排序,如果长度不相等,那么短的排在长的后面。如果返回值为 true
,表示第一个字符串应该排在第二个字符串之前;如果返回值为 false
,则表示第一个字符串应该排在第二个字符串之后。
round35
B.小红的数组分配
题目内容:小红拿到了一个长度为2∗n的数组,她希望你将其中所有元素分配到两个长度相等的数组a和b,满足对于1≤i≤n有ai=bi。你能帮帮她吗?
输入:
2
1 4 4 1
输出:
4 1
4 1
我的代码(数组法):
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, temp = 0;
cin >> n;
vector<int> vec;
for(int i = 0; i < 2 * n; i++) {
cin >> temp;
vec.push_back(temp);
}
sort(vec.begin(), vec.end());
for(int i = 0; i < 2 * n - 1; i += 2) {
if(vec[i] != vec[i+1]) {
cout << "-1";
return 0;
}
}
for(int j = 0; j < 2; j++) {
for(int i = 0; i < 2 * n - 1; i += 2) {
cout << vec[i] << ' ';
}
cout << '\n';
}
return 0;
}
反思:一个两两能配对的数组,例如1 4 1 4,经过sort后会变成1 1 4 4;通过遍历先检查是不是能两两配对的数组,再输出;注意遍历步长为2,自己做的时候就是没有想到这一点所以写的不仅长还不对。
我的代码(map法):
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, temp = 0;
cin >> n;
map<int, int> record;
for(int i = 0; i < 2 * n; i++) {
cin >> temp;
record[temp]++;
}
for(auto it : record) {
if(it.second % 2 != 0) {
cout << -1;
return 0;
}
}
vector<int> res;
for(auto it : record) {
int cnt = it.second / 2;
while(cnt--) {
res.push_back(it.first);
}
}
for(int i = 0; i < 2; i++) {
for(auto i : res) {
cout << i << ' ';
}
cout << '\n';
}
return 0;
}
反思:map左值为该元素的值,右值为为该元素出现的频率,遍历map,频率不为2的倍数直接return;再使用数组res存储去掉搭档后的元素;map平时写的比较少,要注意一下语法。
C.小红关鸡
题目内容:有n个鸡窝排成一排,第i个鸡窝在数轴上的坐标是xi,有一只小鸡会随机的在一个鸡窝中出现。小红准备在数轴上放置两个栅栏,如果小鸡出现在两个栅栏中间(包括端点),则将被小红关住。为了方便管理,两个栅栏之间的最大距离不能超过k。现在小红希望最大化成功“关鸡”的概率,请你帮小红求出这个概率。
我的代码(双指针法):
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, k = 0, temp = 0;
cin >> n >> k;
vector<int> pos;
for(int i = 0; i < n; i++) {
cin >> temp;
pos.push_back(temp);
}
sort(pos.begin(), pos.end());
int l = 0, r = 0, ma = 0;
while(r < n) {
while(pos[r] - pos[l] > k) {
l++;
}
ma = max(ma, r - l + 1);
r++;
}
cout << 1.0 * ma / n;
return 0;
}
反思:题目求的也就是给你给你一段定长k,在k的范围内尽可能圈住多的元素。无论是双指针法还是二分法都要先对数组元素进行sort;然后移动右指针,当双指针覆盖长度大于k时,移动左指针;取k范围内能覆盖的最大长度。
round36
B.小红的小红矩阵构造
题目内容:小红希望你构造一个n行m列的矩阵,满足所有元素之和恰好等于x,且每行、每列的异或和全部相等。你能帮帮她吗?
异或和:将异或的两个数字转换为二进制,比较每一位,相同为0,不同为1
【例】8 xor 12 = 4
1000
1100
异或: 0100
异或和的一个性质:a^b^a = b
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, m = 0, x = 0;
cin >> n >> m >> x;
vector<vector<int>> nums;
int temp_num = 0;
for(int i = 0; i < n; i++) {
vector<int> temp;
for(int j = 0; j < m; j++) {
cin >> temp_num;
temp.push_back(temp_num);
}
nums.push_back(temp);
}
int sum = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
sum += nums[i][j];
}
}
if(sum != x) {
cout << "wrong answer";
return 0;
}
set<int> record;
for(int i = 0; i < n; i++) {
int temp1 = 0;
for(int j = 0; j < m; j++) {
temp1 ^= nums[i][j];
}
record.insert(temp1);
}
for(int j = 0; j < m; j++) {
int temp1 = 0;
for(int i = 0; i < n; i++) {
temp1 ^= nums[i][j];
}
record.insert(temp1);
}
if(record.size() > 1) {
cout << "wrong answer";
return 0;
}
cout << "accepted";
return 0;
}
反思:自己写的时候主要是死在了“每行、每列的异或和全部相等”,既不知道怎么判断该异或和只出现了一次,又不知道怎么判断“每行”、“每列”;现在新学了判断某个值是否只出现了一次就用set,因为set会自动去重;遍历每行就把行遍历放前面,遍历每列就把列遍历放前面;异或运算符为‘^’
round37
C.红魔馆的馆主
题目内容:小红来到了红魔馆。众所周知,红魔馆的馆主是一只495岁的吸血鬼,所以她非常喜欢495这个数。现在,小红拿到了一个正整数,她想在这个正整数的结尾增加尽可能少的数字,使得该数字变成495的倍数。请你给出任意一个添加方案。
代码(这个代码只过了96%,看来是后来数据加强了):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll m = 0, i = 0;
cin >> m;
__int128 n = m;
if(n % 495 == 0) return cout << -1, 0;
for(i = 0; i <= 9; i++) {
if( (n * 10 + i) % 495 == 0) {
return cout << i, 0;
}
}
for(i = 10; i <= 99; i++) {
if( (n * 100 + i) % 495 == 0) {
return cout << i, 0;
}
}
for(i = 100; i <= 999; i++) {
if( (n * 1000 + i) % 495 == 0) {
return cout << i, 0;
}
}
return 0;
}
反思:使用long long有爆表的可能,所以开__int128,能把范围扩大到1e36;然后就是暴力枚举,n如果带上后缀i就能成为495的倍数的话就直接输出,只开三个循环是因为在整数范围内,任意划定一个495的长度就一定能划到495的倍数。
round38
B.小红的抛弃后缀
题目内容:小红拿到了一个正整数,她准备切掉一个后缀并抛弃,使得剩余部分是9的倍数。小红想知道有多少种不同的操作方案?(1<= x <= 10e100000)
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin >> s;
int ans = 0;
int sum = 0;
for(int i = 0; i < s.size(); ++i) {
sum += (s[i] - '0');
if(sum % 9 == 0) ans++;
}
cout << ans;
return 0;
}
反思:这是一个需要记住的结论,就是判断一个数是不是3/9的倍数,就看它的每一位上数字的和是不是3/9的倍数。例如18拆成1+8,9就是9的倍数。然后遍历字符串的每一位,如果当前和是9的倍数就ans++.
C.小红的字符串构造
题目内容:小红希望你构造一个长度为n的、仅包含小写字母的字符串,其中恰好有k个长度大于1的回文子串。你能帮帮她吗?
输入描述:
两个整数n,k,用空格隔开。 1≤n≤10^5 0≤k≤n/2
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n = 0, k = 0;
cin >> n >> k;
string a = "abc";
string b = "def";
string s;
for(int i = 0; i < k; ++i) {
s += a[i%3];
s += a[i%3];
}
for(int i = k*2; i < n; ++i) {
s += b[i%3];
}
cout << s;
return 0;
}
反思:题目条件里k <= n/2,也就是回文串的长度不会超过字符串的长度,那么如果要1个回文串,就可以用“aa”,2个就是“aabb”, 3个就是“aabbcc”,4个就是“aabbccaa”,保证回文串之间不会再次形成回文,余下的部分用“def”一位位填充。
D.小红的平滑值插值
题目内容:小红定义一个数组的“平滑值”为:相邻两数差的绝对值的最大值。现在小红拿到了一个数组。她每次操作可以在两个元素之间添加一个整数(不能添加在第一项前面或者最后一项后面)。小红希望最终数组的平滑值恰好等于k,你能帮小红求出最小的操作次数吗?
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 1e5+9;
int a[MAX];
int n, ma = -1;
long long cnt = 0, k;
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
if(i >=2) {
ma = max(ma, abs(a[i]-a[i-1]));
}
}
if(ma < k) {
cout << 1;
return 0;
}
if(ma == k ) {
cout << 0;
return 0;
}
for(int i = 2; i <= n; ++i) {
if(abs(a[i]-a[i-1]) > k) {
int dis = abs(a[i]-a[i-1])/k;
if(abs(a[i]-a[i-1])%k==0) dis--;
cnt += dis;
}
}
cout << cnt;
return 0;
}
反思:假如数组的平滑值刚好等于k,不用操作直接输出0;假如平滑值<k,任选两数之间插入一个更大的数即可,输出1;假如平滑值比k大,此时我们既希望插入的数字少一点,又得保证其中一个平滑值为k,另外<=k,所以应该每k个大小就插入一个数;
5 0
k = 2
5/2 = 2 ...... 1
此时会插入两个数,形成3段距离;由于插入个数永远为距离-1,所以对除法向上取整再-1就是插入的数字个数
5 1
k = 2
在这个例子里,直接插入5/2个数会得到2,但是插入2个不是最小插入方案,插入1个才是最小插入方案(插入3);所以使用向上取整(整除不会再向上)再-1是最严谨的;最后不要忘了开long long,要形成一种习惯。