第七次CCF-CSP认证(含C++源码)
第七次CCF-CSP认证
- 折点记数
- 思路及满分代码
- 俄罗斯方块
- 思路及满分代码
- 路径解析
- 思路及满分代码
折点记数
题目链接
思路及满分代码
题目给出了折点的定义:
如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。
说白了就是比如说给你一个价格走势图,3.1卖5块 3.2号涨价了卖6块,3.3号又降价了卖5块,那3.2号就是一个折点,假如3.2号卖4块 它也是一个折点,而且题目明确说了是一个点 不需要考虑类似蓄水问题中坑的出现
思路:遍历一下输入的数组,比较一下每个点的左右两端就好了 用一个计数器记录一下
需要注意的就是溢出的问题,因为如果不控制的话,第一个点就会溢出因为i-1不存在
#include <bits/stdc++.h>
using namespace std;
const int N =1010;
int s[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s[i];
}
int res=0;
for(int j=1;j+1<n;j++)
{
int a=s[j-1],b=s[j],c=s[j+1];
if(b>a && b>c || b<a && b<c)
{
res++;
}
}
cout<<res;
}
俄罗斯方块
题目链接
思路及满分代码
其实要解决这个问题的关键就是,我们给定的一个的cube在下落的时候会不会卡住,一般玩过俄罗斯方块的都能明白我在说什么,也就是在遇到边界或者有方块之后就卡住了,其实也就是在问我们如何去判断重叠,举个例子,一个方块一直一直下落,直到与原有的方块发生重叠的时候,假设是t时刻 那么我们方块刚刚好卡住的时刻就是t-1,我们只要输出上一时刻的棋盘上的方块放置情况就可以了
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
// 定义常量 N,用于表示数组的大小,这里主要用于 s 数组和 ss 数组的行和列的最大索引
const int N = 17;
// s 数组用于存储游戏区域的初始状态,ss 数组用于临时存储每次尝试放置方块后的状态
int s[N][N], ss[N][N];
// cube 数组用于存储要放置的 4x4 方块的形状
int cube[4][4];
// 函数 draw 用于尝试在游戏区域的指定位置 (x, y) 放置方块
// 如果放置后出现重叠(即某个位置的值变为 2),则返回 true,否则返回 false
bool draw(int x, int y)
{
// 将 s 数组的内容复制到 ss 数组中,以便在 ss 数组上进行修改,不影响原始的 s 数组
memcpy(ss, s, sizeof ss);
// 遍历 4x4 的方块数组
for (int i = 0; i < 4; i ++)
for (int j = 0; j < 4; j ++)
{
// 如果当前方块位置的值为 1,表示该位置有方块
if (cube[i][j])
{
// 计算该方块在游戏区域中的实际位置
int a = x + i, b = y + j;
// 在 ss 数组中对应位置的值加 1
ss[a][b] ++;
// 如果该位置的值变为 2,说明出现了重叠,返回 true
if (ss[a][b] == 2) return true;
}
}
// 没有出现重叠,返回 false
return false;
}
int main()
{
// 读取游戏区域的初始状态,游戏区域为 15 行 10 列
for (int i = 0; i < 15; i ++)
for (int j = 0; j < 10; j ++)
cin >> s[i][j];
// 读取要放置的 4x4 方块的形状
for (int i = 0; i < 4; i ++)
for (int j = 0; j < 4; j ++)
cin >> cube[i][j];
// 在游戏区域的第 15 行的所有位置设置为 1,表示游戏区域的底部边界
for (int j = 0; j < 10; j ++) s[15][j] = 1;
// 读取方块放置的初始列边界
int boundary;
cin >> boundary;
// 由于数组索引从 0 开始,所以将边界值减 1
boundary --;
// 从第 0 行开始,不断尝试向下放置方块
for (int i = 0; ; i ++)
{
// 尝试在当前行 (i) 和指定列 (boundary) 放置方块
if (draw(i, boundary))
{
// 如果放置后出现重叠,说明上一行是可以放置的位置,再次调用 draw 函数放置方块
draw(i - 1, boundary);
// 跳出循环
break;
}
}
// 输出放置方块后游戏区域的状态,只输出前 15 行
for (int i = 0; i < 15; i ++)
{
for (int j = 0; j < 10; j ++)
cout << ss[i][j] << " ";
cout << endl;
}
return 0;
}
路径解析
题目链接
样例:
思路及满分代码
这题是基于linux系统所写,所以对于跟我一样没有学过的朋友接受程度上可能就有些差强人意了,这次我看了一下平均分就100/500,也就是人均就只能A一道的水平,大可不用因为做不出来而焦虑 我用AI生成了注释和例子帮助大家理解,代码思路:yxc
代码思路文字版:
路径分割:get 函数负责将输入路径字符串按 / 分割,去除 / 并提取路径片段,存入容器。例如输入 a/b/c,处理后得到 {“a”, “b”, “c”}。
路径解析:walk 函数处理路径片段,遇到 . 忽略(表示当前目录),遇到 … 则回退上一级目录(若当前路径非空),其他片段直接加入当前路径。最后根据处理结果输出标准路径。
主流程:读取输入的路径数量和初始相对路径,然后逐行处理每个待转换的路径。根据路径是否以 / 开头,判断是绝对路径还是相对路径,分别调用 walk 处理并输出结果。
举例
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
// 功能:将输入字符串中的'/'去除,分割路径片段存入vector
// 示例:输入"a/b/c",返回{"a", "b", "c"}
vector<string> get(string str)
{
vector<string> res; // 存储分割后的路径片段
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '/') continue; // 跳过'/'字符
int j = i + 1;
// 找到下一个'/'的位置,确定当前路径片段的结束位置
while (j < str.size() && str[j] != '/') j++;
// 截取当前路径片段(从i到j-1),存入res
res.push_back(str.substr(i, j - i));
i = j; // 移动i到当前处理片段的末尾,避免重复处理
}
return res;
}
// 功能:根据当前路径cur,解析处理路径path
// cur:当前路径(如相对路径的基准路径)
// path:待处理的路径片段集合
void walk(vector<string> cur, vector<string> path)
{
for (auto c : path)
{
if (c == ".") continue; // 遇到"."(当前目录),直接忽略
else if (c == "..")
{
// 遇到".."(返回上一级),若当前路径非空,弹出最后一个元素
if (cur.size()) cur.pop_back();
}
else {
// 其他正常路径片段,加入当前路径
cur.push_back(c);
}
}
// 输出处理后的路径
if (cur.empty())
{
puts("/"); // 路径为空,说明是根目录,输出"/"
return;
}
// 拼接路径,每个片段前加'/'
for (auto p : cur)
cout << "/" << p;
cout << endl;
}
int main()
{
int n;
string str;
cin >> n >> str;
getchar(); // 过滤输入n和str后的换行符
// 处理初始相对路径,cur存储相对路径的片段
vector<string> cur = get(str), ab;
while (n--)
{
getline(cin, str); // 读取每行待转换的路径
auto path = get(str); // 分割路径片段
// 判断是否为绝对路径:以'/'开头
if (str.size() && str[0] == '/') walk(ab, path);
else walk(cur, path); // 相对路径,基于cur处理
}
return 0;
}