写在前面
-
这是一篇友好的解题报告
-
为响应勤俭节约的核心价值观,题意省略背景,使用简洁中文
-
为了便于理解,每段思路下都会有代码
-
每题讲解一种个人认为比较简单易懂的方法
-
每题的标题均链接了洛谷(众所周知,洛谷的题解很友好)或者个人认为比较好的题解
-
本篇题解仅供参考,有时间的同学可以通过搜索题目了解每题更多的解法学习
-
由任何问题均可以通过
qq,询问我 -
由于匆忙赶制,所以可能会有错误或用语不规范请谅解
CF144A Arrival of the General
题意
-
输入数组大小
n( 2 < = n < = 100 ) (2<=n<=100) (2<=n<=100), -
依次输入
a1,a2,a3......an( 1 < = a < = 100 ) (1<=a<=100) (1<=a<=100) -
求经过几次交换(只可以前后相邻的两个交换) 保证第一个数最大,最后一个数最小(中间的大小没影响)
思路
首先我们可以通过遍历找到最左边的最大值ma_val和最右的最小值mi_val
即为ma_pos和mi_pos
int mi_pos, ma_pos, mi_val, ma_val;
mi_val = a[1], mi_pos = 1;
for (int i = 2; i <= n; i++)
if (a[i] <= mi_val) //由于需要找到最右边的最小值,所以可以等于
mi_val = a[i], mi_pos = i;
ma_val = a[1], ma_pos = 1;
for (int i = 2; i <= n; i++)
if (a[i] > ma_val)
ma_val = a[i], ma_pos = i;
由于只能相邻的交换,所以
我们只需要先将最小值交换到n,再将最大值交换到1即为最小次数
由于每次交换后,他的位置变化为1,所以从x交换到y的步数为
y
−
x
y-x
y−x
(
y
>
x
)
(y>x)
(y>x)
要注意当mi_pos<ma_pos时,两者交换的过程会有交汇,ma_pos会被向前交换一位
int ans = 0;
ans += (n - mi_pos); //最小值交换到n
if(mi_pos<ma_pos)
ma_pos--;
ans += ma_pos - 1; //最大值交换到1
cout << ans << '\n';
复杂度 O ( n ) O(n) O(n)
CF469A I Wanna Be the Guy
题意
小X和小Y最近在玩一个叫做I wanna be the guy的游戏,这个游戏有n个关卡。把n个关卡都通过后,那么就完成游戏了。
可是小X只能过p关,小Y只能过q关。因为他们能力有限,所以他们决定合作。
给出小X和小Y能通过的关卡,他们能完成这个游戏吗?如果可以,输出I become the guy.,否则,输出Oh, my keyboard!
思路
读入p个数字和q个数字,判断1到n是否都出现过即可
我们可以开一个标记数组vis[x],表示x这个数是否出现过
我们遍历
p
+
q
p+q
p+q个数字,对于每个数字x,
v
i
s
[
x
]
+
+
vis[x]++
vis[x]++表示x这个数字出现过一次
for (int i = 1; i <= p; i++) //遍历p个数
vis[a[i]]++;
for (int i = 1; i <= q; i++) //遍历q个数
vis[b[i]]++;
最后,我们查找vis[1]到vis[n]是否有一个为0即可
for (int i = 1; i <= n; i++)
{
if(vis[i]==0) //只要找到一个,直接退出一个
{
printf("Oh, my keyboard!");
return 0;
}
}
printf("I become the guy.\n"); //没有一个为0,即可通关
return 0;
复杂度 O ( n ) O(n) O(n)
杨辉三角
题意
多组测试,输出杨辉三角形
思路
杨辉三角形规律(即为组合数规律)
每行第一个和最后一个为1(
c
o
m
b
[
i
]
[
0
]
=
c
o
m
b
[
i
]
[
i
]
=
1
comb[i][0]=comb[i][i]=1
comb[i][0]=comb[i][i]=1)
中间的每个数等于它上方两数之和( c o m b [ i ] [ j ] = c o m b [ i − 1 ] [ j − 1 ] + c o m b [ i − 1 ] [ j ] comb[i][j]=comb[i-1][j-1]+comb[i-1][j] comb[i][j]=comb[i−1][j−1]+comb[i−1][j])
需要注意的是杨辉三角形包含第0行,为1个1
我们可以先计算出前30行的组合数,当询问时输出即可(预处理)
for (int i = 0; i < 30; i++)
{
comb[i][0] = comb[i][i] = 1;
for (int j = 1; j < i; j++)
comb[i][j] = comb[i - 1][j - 1] + comb[i - 1][j];
}
多组输入,每行最后一个数字后面没有空格
注意输出格式,每个杨辉三角形后面都有一个空行
while (cin >> n)//多组输入
{
for (int i = 0; i < n; i++)//从第0行开始输出
{
for (int j = 0; j < i; j++)
cout << comb[i][j] << ' ';
cout << comb[i][i] << '\n'; //最后一个数直接输出换行
}
cout << '\n';//每个三角形均输出空行
}
return 0;
复杂度 O ( n 2 ) O(n^2) O(n2)
CF208A Dubstep
题意
Vasya有一句歌词(可能含有空格)
他在第一个单词之前和最后一个单词之后加上若干(可以为0)个"WUB"
再每个单词之间加上若干(至少为1)个"WUB",转变成一个新的字符串(无空格)
如"I AM X" 可以变成 "WUBWUBIWUBAMWUBWUBX" 而不能变成 "WUBWUBIAMWUBX" 现在给出转变后的字符串,求出原来的字符串
思路
本题为字符串处理题,不懂字符数组或String同学可以预习下这一部分
这里讲字符数组的一种解法
字符数组简介(由于简介所以部分用语并不规范,请谅解)
char s[1000]:定义字符数组
cin>>s:可以读入字符数组
strlen(s):可以得到字符数组的长度
cout<<s:可以输出字符数组
s[i]:可以直接访问第i个字符(第一个下标为0,与数组相同)
注意的是strlen,cout都需要字符数组的最后一位为\0
我们可以遍历字符数组,
-
遇到
WUB,将WUB跳过 -
将非
WUB的字符写入一个新的字符串Str遇到下一个
WUB时,说明新字符串读入结束,将他输出即可
cin >> s; //读入字符串
int k = 0;
int len = strlen(s); //得到字符串的长度
for (int i = 0; i < len;)
{
if(s[i]=='W'&&s[i+1]=='U'&&s[i+2]=='B')//遇到WUB
{
i += 3;//跳过这三个字符
if(k>0)//k>0表示已经记录新的字符串
{
str[k] = 0;//保持字符数组的最后一位为0
cout << str << ' ';
k = 0;//将k归0
}
}
else
str[k++] = s[i], i++; //将新的字符加入str字符串,并跳到下一位
}
if(k>0) //最后一个可能也是字符串,没遇到WUB,没输出
{
str[k] = 0;
cout << str << ' ';
}
复杂度 O ( n ) O(n) O(n)
CF61A Ultra-Fast Mathematician
题意
-
给你两个字符串,每个字符串都代表一个二进制下的数
-
求这两个数异或起来后的值的二进制是多少。
-
保证给的两个字符串一样长,也就是说可能有字符串会有前导0,同时请保证输出的这个二进制数一样长,也就是说不要去掉前导0
-
字符串长度小于100
思路
解释一下异或的意思
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:
0
⊕
0
=
0
,
1
⊕
0
=
1
,
0
⊕
1
=
1
,
1
⊕
1
=
0
0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0
0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(相同为0,不同为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法
所以,我们只要将两个字符串输入,比较每一位,生成一个新的字符串输出即可
若 a [ i ] = = b [ i ] a[i]==b[i] a[i]==b[i],则 c [ i ] = 0 c[i]=0 c[i]=0,若 a [ i ] ! = b [ i ] a[i]!=b[i] a[i]!=b[i],则 c [ i ] = 1 c[i]=1 c[i]=1
字符数组简介见上题
cin >> a >> b; //读入两个字符串
int len = strlen(a); //得到字符串长度
for (int i = 0; i < len; i++) //遍历两个字符串
if (a[i] == b[i])
c[i] = '0'; //相同新字符串该位为'0'
else
c[i] = '1'; //反之改位为1
c[len] = 0; //最后一位保持0
cout << c;
return 0;
复杂度 O ( n ) O(n) O(n)
F - To and Fro
题意:找出字符串的存储规律,输出就行了:

思路
我们可以发现奇数行是正序的,偶数行是倒序的
我们可以n个,n个遍历数组,将字符加入二维字符数组,还原二维数组
cin >> s;
int len = strlen(s); //得到长度
int m = len / n; //m为二维数组行数
int k = 0; //k为查询的字符数组的第几位
for (int i = 1; i <= m; i++)//矩阵的第i行
{
if (i % 2 == 1)//奇数行
{
for (int j = 1; j <= n; j++)//正序填入
a[i][j] = s[k++];
}
else
{
for (int j = n; j >= 1; j--)//倒序填入
a[i][j] = s[k++];
}
}
按题意,竖着输出字符即可
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cout << a[j][i];
cout << '\n';
复杂度$O(n) $
G - 单词数
题意
多组输入
每组一行,每行有多个单词,统计每行不同单词的个数
以#结束
思路
此题对于刚接触acm的同学较难
这里介绍一种比较简单的方法
我们可以通过getchar()连续读入字符
-
当读入小写字母时,将他加入新的字符串
str(string 可以直接通过加法连接字符)- (不懂string可以点击标题链接,学习字符数组的解法或者学习string)
-
当读入非小写字母时
- 说明一个字符串输入完毕,将
str加入map(听说yx学姐已经给你们讲过map了) - 若读入
\n换行符,说明一行已经读入完毕,输出map.size()
- 说明一个字符串输入完毕,将
map<string, bool> Map;
string str;
char ch;
while ((ch = getchar()) != '#') {
if (ch >= 'a' && ch <= 'z') {
str += ch;
} else {
if (str.size() > 0) {
Map[str] = true;
str = "";
}
if (ch == '\n') {
cout << Map.size() << endl;
Map.clear();
str = "";
}
}
}
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
H - 不要62
题意
- 杭州认为一个数字中包含
4或者62是不吉利的 - 询问从
n到m包含几个吉利的数字
思路
需要判断一个数字是否位吉利数字,我们需要遍历她的每一位
对于x,我们通过x%10可以得到x的最后一位,然后x/10将最后一位消除
如此我们就得到一个数字的最后一位,重复以上过程值得x==0,我们就能得到一个数字的每一位
int a[7], k = 0;
while (x) {
a[k++] = x % 10;
x /= 10;
}
显然,我们的得到的数字是倒序的,所以不要62就变成了不要26
我们遍历即可判断一个数是否吉利
bool check(int x){
int a[7], k = 0;
while(x){
a[k++] = x % 10;
x /= 10;
}
a[k] = 0;
for(int i=0;i<k;i++)
if(a[i]==4)
return false;
else if(a[i]==2&&a[i+1]==6)
return false;
return true;
}
对于每组询问都遍历n到m是不现实的,想想都会TLE
所以我们可以暴力预处理好每一位数是否吉利
然后用前缀和优化,即用sum[i]表示0到i有多少个吉利的数
那么n到m有多少个吉利数即为
s
u
m
[
m
]
−
s
u
m
[
n
−
1
]
sum[m]-sum[n-1]
sum[m]−sum[n−1]
for (int i = 1; i <= 1000000; i++)
if (check(i))
sum[i] = sum[i - 1] + 1;
else
sum[i] = sum[i - 1];
int n, m;
while (cin >> n >> m) {
if (n == 0 && m == 0)
break;
cout << sum[m] - sum[n - 1] << '\n';
}
复杂度 O ( n ) O(n) O(n)
I - 超级楼梯
题意
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?
思路
我们可以倒过来想,对于第i台阶,只有可能由i-1阶或i-2阶迈上
(
i
>
=
3
)
(i>=3)
(i>=3)
所以迈上第i阶的方案为,迈上第i-1阶的方案加上迈上第i-2阶的方案
显然第1,2阶方案均为1,细心的同学一定已经发现这就是斐波那契数列了
我们只需要预处理1到40的方案数,返回询问的值即可
int fib[45] = { 0, 1, 1 };
for (int i = 3; i <= 40; i++)//计算fib数
fib[i] = fib[i - 1] + fib[i - 2];
int t, n;
cin >> t;
while (t--) {
cin >> n;
cout << fib[n] << '\n';
}
复杂度 O ( n ) O(n) O(n)
J - Fibsieve`s Fantabulous Birthday
题意
-
给出一个矩阵,该矩阵以左下角为中心绕圈填数
-
询问数字
n所在的坐标
思路

我们观察到
- 对角线上的数为 n ∗ ( n − 1 ) + 1 n*(n-1)+1 n∗(n−1)+1
- 每圈上的数刚好介于两个平方数中间,即只要求出平方根,即可知道层数
上面两个规律是为什么? 可以思考下
-
以
x=1为起点,对于奇数层上的数递减,偶数层数上的数递增 -
分类讨论,以对角线上的点为中心点,计算
n与对角的差距即可 -
注意特殊处理平方数和
long long
cin >> n;
long long base = sqrt(n * 1.0); //平方根
if (base * base == n) { //平方数
if (base & 1)
cout << "1 " << base << '\n';
else
cout << base << ' ' << 1 << '\n';
continue;
}
long long limit = base * (base + 1) + 1;//对角线上的数
if (base & 1) { //奇数层
if (n < limit)
cout << base + 1 - (limit - n) << ' ' << base + 1 << '\n';
else
cout << base + 1 << ' ' << base + 1 - (n - limit) << '\n';
} else { //偶数层
if (n < limit)
cout << base + 1 << ' ' << base + 1 - (limit - n) << '\n';
else
cout << base + 1 - (n - limit) << ' ' << base + 1 << '\n';
}
K - Train Problem I
题意
给出火车站的火车入站顺序和出站顺序
火车站只有一个门,请问是否可能?
思路
模拟栈
因为火车站只有一个门,所以和栈的结构是一样的
后进站的火车必须先出,在里面的火车出不来
所以我们发现这和栈是一样的
栈简介:栈中元素先进后出
st.push(val):将val加入栈
st.top():返回栈顶元素的值
st.pop():弹出栈顶的元素
st.empty():判断栈是否为空
我们知道入栈顺序和出栈顺序
我们可以模拟这个过程
- 先按序将火车压进栈,并比较当前栈顶与出栈顺序
- 若当前栈顶元素为下一个出栈的元素,那么他必须马上出栈
- 因为当下一个元素进栈,第一个出栈的元素只能栈顶元素,产生矛盾
- 当栈顶元素相同时,要将能出栈的都出栈,而不是只比较一个
- 若最后栈为空,表示成功,否则失败
- 过程用数组记录,操作的过程,如
1表示入栈,2表示出栈
scanf("%s%s", s1, s2); //入栈和出栈顺序
int k = 0, j = 0; //j为下一个出栈元素的下标
for (int i = 0; i < n; i++) {
st.push(s1[i]); //入栈
res[k++] = 1; //记录入栈
while (!st.empty() && st.top() == s2[j]) {//若栈顶元素需要出栈
st.pop();
res[k++] = 2; //记录出栈
j++;
}
}
if (!st.empty()) {
cout << "No.\n";
while (!st.empty())
st.pop();
} else {
cout << "Yes.\n";
for (int i = 0; i < k; i++)
if (res[i] == 1)
cout << "in\n";
else
cout << "out\n";
}
cout << "FINISH\n";
复杂度: O ( n ) O(n) O(n)
L - ACboy needs your help again!
题意
给出操作方式,FIFO(先进先出)或FILO(先进后出)
请输出out的数
思路
先进先出:队列
先进后出:栈
队列:队列中元素先进先出
q.push(val):将val加入队列
q.front():返回队首元素的值
q.pop():弹出队首的元素
模拟,他的操作即可
FIFO(队列模拟)
queue<int> q;
while (n--) {
cin >> opt;
if (opt[0] == 'I') { //in
cin >> x;
q.push(x); //入队
} else { //out
if (!q.empty()) { //不为空
cout << q.front() << '\n';
q.pop();
} else
cout << "None\n";
}
}
FILO(栈模拟)
stack<int> st;
while (n--) {
cin >> opt;
if (opt[0] == 'I') {
cin >> x;
st.push(x);
} else {
if (!st.empty()) {
cout << st.top() << '\n';
st.pop();
} else
cout << "None\n";
}
}
复杂度$O(n) $
M - Ugly Numbers
题意
题目要求我们输入一个数字n,输出第n个丑数,丑数的要求是为其质数因子仅有2、3、5。
思路
这里提供一个用set暴力的思路
题目链接了一个更精妙的思路
由于质因子只有2,3,5,所以对于每个数x,2*x,3*x,5*x均为丑数
由于我们要找前1500小的,我们想到一个STL:set,带有去重和排序功能
set简介:set可以对加入的数据去重和排序
s.insert(val):插入值为val的元素
s.clear():清空set
s.size():set的大小
set<T>::iterator iter:迭代器iter
set的遍历需要使用迭代器iter
set<int>::iterator iter;
for (iter = s.begin(); iter !s.end(); iter++)
cout << *iter << ' ';
-
我们每次只要找到最小的值
x,将2*x,3*x,5*x加入, -
再次找到剩余数中的最小值重复以上步骤即可
原理相当于做个完全背包(qwq),当然显然也是对的
set<long long> Set;
Set.insert(1);
set<long long>::iterator iter = Set.begin();
for (int i = 1; i <= 1500; i++, iter++) {
res[i] = *iter;
Set.insert(res[i] * 2);
Set.insert(res[i] * 3);
Set.insert(res[i] * 5);
}
在提供一种优先队列的写法,需要去重
priority_queue<long long, vector<long long>, greater<long long> > q;
//优先队列,以小的为优先
q.push(1);
for (int i = 1; i <= 1500; i++) {
while (q.top() == res[i - 1])//去重
q.pop();
res[i] = q.top();
q.pop();
q.push(res[i] * 2);
q.push(res[i] * 3);
q.push(res[i] * 5);
}
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
::iterator iter:迭代器iter`
set的遍历需要使用迭代器iter
set<int>::iterator iter;
for (iter = s.begin(); iter !s.end(); iter++)
cout << *iter << ' ';
-
我们每次只要找到最小的值
x,将2*x,3*x,5*x加入, -
再次找到剩余数中的最小值重复以上步骤即可
原理相当于做个完全背包(qwq),当然显然也是对的
set<long long> Set;
Set.insert(1);
set<long long>::iterator iter = Set.begin();
for (int i = 1; i <= 1500; i++, iter++) {
res[i] = *iter;
Set.insert(res[i] * 2);
Set.insert(res[i] * 3);
Set.insert(res[i] * 5);
}
在提供一种优先队列的写法,需要去重
priority_queue<long long, vector<long long>, greater<long long> > q;
//优先队列,以小的为优先
q.push(1);
for (int i = 1; i <= 1500; i++) {
while (q.top() == res[i - 1])//去重
q.pop();
res[i] = q.top();
q.pop();
q.push(res[i] * 2);
q.push(res[i] * 3);
q.push(res[i] * 5);
}
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
本文精选多道经典算法题目,覆盖数组操作、字符串处理、数据结构应用等核心内容,详细解析题意与解题思路,附带代码实现,旨在帮助读者深入理解算法原理,提升编程技能。

364

被折叠的 条评论
为什么被折叠?



