寒假acm集训第一讲笔记
寒假acm集训第一讲笔记
题目网址:https://vjudge.net/article/7420
第一题 Long Loong
很简单的循环语句,没啥好说的。
#include <iostream>
using namespace std;
int main()
{
int N;
cin >> N;
cout << "L";
for (int i = 0; i < N; i++)
{
cout << "o";
}
cout << "ng";
return 0;
}
第二题 YES or YES?
首先因为char类型本质是int类型,用ASCII码把大小写统一,然后判断即可。
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
char s[5];
for (int i = 0; i < t; i++)
{
cin >> s;
for (int j = 0; j < 3; j++)
{
if (s[j] >= 97 && s[j] <= 122)
s[j] -= 32;
}
if (s[0] == 'Y' && s[1] == 'E' && s[2] == 'S')
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
第三题 Even? Odd? G
奇偶性只需要判断个位数即可,而且题目给的数据范围是每个正整数不超过10^60,明显不是一般的int或long long能够处理的,所以我们先以字符串形式输入,然后用strlen得到字符串长度后,找到个位的那个字符,同样利用char类型本质是int类型(ASCII码),再判断个位是否偶数即可。
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
char s[63];
cin >> s;
int a;
int L = strlen(s);
a = s[L - 1] - '0';
if (a % 2 == 0)
cout << "even" << endl;
else
cout << "odd" << endl;
}
return 0;
}
第四题 Problem Generator
有m轮,那么'A'、'B'、'C'、'D'、'E'、'F'和'G'就分别要有m个,我使用count [ i ] (i = 0 ~ 6)来分别表示'A'、'B'、'C'、'D'、'E'、'F'和'G'还需要提出多少次,初始化均为m。
接下来每输入一个字母,在保证count [ i ] 不会出现负数的情况下,count [ i ] --
最后用sum累加count [ i ] 即可。
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int t;
cin >> t;
for (int i = 0; i < t; i++)
{
int n, m;
cin >> n >> m;
int count[7];
for (int k = 0; k < 7; k++)
count[k] = m;
char s[55];
cin >> s;
for (int j = 0; j < n; j++)
{
if (count[s[j] - 'A'] > 0)
count[s[j] - 'A']--;
}
int sum = 0;
for (int h = 0; h < 7; h++)
sum += count[h];
cout << sum << endl;
}
return 0;
}
第五题 rules
用num_k记录某一天遵守规则k的人数
用count记录规则k符合名意的天数
另外注意判断条件是“大于等于一半”,所以奇偶数要分开计算
其余没啥好说的。
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int n, m, k, count = 0;
cin >> n >> m >> k;
for (int i = 0; i < m; i++)
{
int num_k = 0;
for (int j = 0; j < n; j++)
{
int a;
cin >> a;
if (a == k)
num_k++;
}
if (n % 2 == 0)
if (num_k >= n / 2)
count++;
if (n % 2 != 0)
if (num_k > n / 2)
count++;
}
if (m % 2 == 0)
{
if (count >= m / 2)
cout << "YES";
else
cout << "NO";
}
if (m % 2 != 0)
{
if (count > m / 2)
cout << "YES";
else
cout << "NO";
}
return 0;
}
第六题 Many Replacement
- 存储所有操作:
- 先把所有操作 ((c_i, d_i)) 读取并保存下来,不要立刻在字符串上做替换。
- 倒序构造
finalMap:finalMap[x]表示字母'a' + x最后会被替换成哪个字母(以整数 0~25 表示,0 对应'a',1 对应'b',…)。- 初始时
finalMap[x] = x,表示所有字母默认保持自己。 - 从后往前遍历操作:
- 如果本次操作是“将
c替换成d”,那么我们就让finalMap[c] = finalMap[d]。这样等价于 “c的最终去向 =d的最终去向”。
- 如果本次操作是“将
- 一次性替换字符串:
- 遍历字符串的每个字符,根据
finalMap把它映射成“最终字符”,得到结果后输出。
- 遍历字符串的每个字符,根据
时间复杂度:
- 构造
finalMap阶段,遍历 (Q) 次操作,每次操作 (O(1)),合计 (O(Q))。 - 最终替换字符串,遍历 (N) 个字符,合计 (O(N))。
- 总体 (O(N + Q)),足以应对 (N, Q \leq 2\times 10^5) 的规模。
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q;
cin >> N;
string S;
cin >> S;
cin >> Q;
// 用于记录 Q 次操作
vector<pair<char, char>> ops(Q);
for (int i = 0; i < Q; i++)
{
char c, d;
cin >> c >> d;
ops[i] = {c, d};
}
// finalMap[x] 表示字母 x(0~25) 最终会被替换成哪个字母(0~25)
// 初始情况下,每个字母都映射为它自己
vector<int> finalMap(26);
iota(finalMap.begin(), finalMap.end(), 0);
// 相当于 for (int i=0; i<26; i++) finalMap[i] = i;
// 从后往前处理操作
for (int i = Q - 1; i >= 0; i--)
{
char c = ops[i].first; // 被替换的字母
char d = ops[i].second; // 替换成的字母
// 注意把字符转成 0~25 的数字
int ic = c - 'a';
int id = d - 'a';
// 让 'c' 的最终指向 = 'd' 的最终指向
finalMap[ic] = finalMap[id];
}
// 最后遍历字符串,按照 finalMap 映射
for (int i = 0; i < N; i++)
{
int x = S[i] - 'a'; // 当前字符对应的索引
int nx = finalMap[x]; // 它最终要变成的字母索引
S[i] = char(nx + 'a'); // 映射回字符
}
cout << S << "\n";
return 0;
}
第七题 更好的交换(比较长)
1、初次题解
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m, a[102][102], row[102], col[102]; // row行column列
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
cin >> a[i][j];
}
for (int i = 1; i <= n; i++)
{
row[i] = i;
col[i] = i;
}
for (int i = 0; i < m; i++)
{
int op, x, y;
cin >> op >> x >> y;
if (op == 0) // 列
{
int t;
t = col[x];
col[x] = col[y];
col[y] = t;
}
if (op == 1) // 行
{
int t;
t = row[x];
row[x] = row[y];
row[y] = t;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cout << a[row[i]][col[j]] << " ";
}
cout << endl;
}
return 0;
}
虽然已经用了懒惰映射,但是运行超时了
2、修改后AC代码
后来加了两行提高效率的代码,并且把数组设成[n+1][n+1]就行了
#include <iostream>
using namespace std;
int main()
{
// 加这两行能提高运行效率
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
int a[n + 1][n + 1], row[n + 1], col[n + 1]; // row行column列
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
cin >> a[i][j];
}
for (int i = 1; i <= n; i++)
{
row[i] = i;
col[i] = i;
}
for (int i = 0; i < m; i++)
{
int op, x, y;
cin >> op >> x >> y;
if (op == 0) // 列
swap(col[x], col[y]);
if (op == 1) // 行
swap(row[x], row[y]);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cout << a[row[i]][col[j]] << " ";
}
cout << endl;
}
return 0;
}
后面我也问了问gpt,也贴上来吧
3、gpt代码
代码分析
时间复杂度分析:
-
输入数据处理:
- 读取矩阵
a的时间复杂度为 ( O(n^2) )。 - 读取并初始化
row和col数组的复杂度为 ( O(n) )。 - 总计:( O(n^2 + n) \approx O(n^2) )。
- 读取矩阵
-
操作处理:
- 进行
m次操作,每次交换数组元素,交换操作为 ( O(1) )。 - 总计:( O(m) )。
- 进行
-
输出结果:
- 遍历输出矩阵,时间复杂度为 ( O(n^2) )。
综合时间复杂度:
[ O(n^2) + O(m) + O(n^2) = O(n^2 + m) ]
如果 ( m ) 非常大,可能会影响性能,但最主要的时间消耗仍在矩阵遍历阶段。
空间复杂度分析:
- 矩阵
a需要 ( O(n^2) ) 空间。 row和col数组分别需要 ( O(n) ) 空间。- 变量
op, x, y等需要常数 ( O(1) ) 空间。
综合空间复杂度:
[ O(n^2 + n) \approx O(n^2) ]
矩阵存储占据主要空间。
优化建议
-
减少输出矩阵遍历:
- 目前代码在输出时按照
row[i]和col[j]进行两次索引,这导致随机访问影响性能。可以考虑直接修改矩阵或延迟索引计算以减少查询次数。
- 目前代码在输出时按照
-
使用懒惰映射 (Lazy Mapping):
- 通过记录行列的映射,避免真正交换,直接按映射顺序输出。该方案已经在代码中使用,但进一步减少查询时的索引操作开销可以提高性能。
-
优化输入输出 (I/O 优化):
- 替换
cin/cout为scanf/printf或ios::sync_with_stdio(false); cin.tie(0);以加快输入输出处理。
- 替换
-
减少矩阵访问:
- 若只需要查询特定行列而非完整矩阵输出,可尝试仅在查询时计算映射关系,避免存取整个矩阵。
改进代码示例:
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> a(n + 1, vector<int>(n + 1));
vector<int> row(n + 1), col(n + 1);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
cin >> a[i][j];
}
iota(row.begin(), row.end(), 0);
iota(col.begin(), col.end(), 0);
for (int i = 0; i < m; i++)
{
int op, x, y;
cin >> op >> x >> y;
if (op == 0)
swap(col[x], col[y]);
else
swap(row[x], row[y]);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cout << a[row[i]][col[j]] << " ";
}
cout << '\n';
}
return 0;
}
优化点:
- 使用
vector代替数组,提高可读性与灵活性。 - 使用
iota快速初始化行列数组。 - 添加
ios::sync_with_stdio(false); cin.tie(0);以提高 I/O 性能。
结论
-
时间复杂度优化目标:
将整体保持在 ( O(n^2 + m) ),减少不必要的矩阵访问。 -
空间复杂度优化目标:
由于矩阵本身必须存储,无法降低到 ( O(n) ),但可减少额外的辅助空间使用。
按照以上建议,能够在大数据输入情况下减少运行时间并避免超时问题。
浙公网安备 33010602011771号