ccf-csp 2016春季真题题解


  1. 折点计数
    问题描述
      给定n个整数表示一个商店连续n天的销售量。如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。其他的天都不是折点。如下图中,第3天和第6天是折点。
    在这里插入图片描述
      给定n个整数a1, a2, …, an表示销售量,请计算出这些天总共有多少个折点。
      为了减少歧义,我们给定的数据保证:在这n天中相邻两天的销售量总是不同的,即ai-1≠ai。注意,如果两天不相邻,销售量可能相同。
    输入格式
      输入的第一行包含一个整数n。
      第二行包含n个整数,用空格分隔,分别表示a1, a2, …, an。
    输出格式
      输出一个整数,表示折点出现的数量。
    样例输入
    7
    5 4 1 2 3 6 4
    样例输出
    2
    评测用例规模与约定
      所有评测用例满足:1 ≤ n ≤ 1000,每天的销售量是不超过10000的非负整数。

代码:

#include <iostream>

using namespace std;

const int MAXN = 1010;
int arr[MAXN];

int main(){
    int n, cnt = 0;

    cin >> n;
    for(int i = 0; i < n; i ++){
        cin >> arr[i];
        if(i > 1){
            if((arr[i - 1] > arr[i - 2] && arr[i - 1] > arr[i]) || (arr[i - 1] < arr[i - 2] && arr[i - 1] < arr[i]))
                ++ cnt;
        }
    }
    cout << cnt;
    return 0;
}

  1. 俄罗斯方块
    问题描述
      俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏。
      游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块。每一轮,都会有一个新的由4个小方块组成的板块从方格图的上方落下,玩家可以操作板块左右移动放到合适的位置,当板块中某一个方块的下边缘与方格图上的方块上边缘重合或者达到下边界时,板块不再移动,如果此时方格图的某一行全放满了方块,则该行被消除并得分。
      在这个问题中,你需要写一个程序来模拟板块下落,你不需要处理玩家的操作,也不需要处理消行和得分。
      具体的,给定一个初始的方格图,以及一个板块的形状和它下落的初始位置,你要给出最终的方格图。
    输入格式
      输入的前15行包含初始的方格图,每行包含10个数字,相邻的数字用空格分隔。如果一个数字是0,表示对应的方格中没有方块,如果数字是1,则表示初始的时候有方块。输入保证前4行中的数字都是0。
      输入的第16至第19行包含新加入的板块的形状,每行包含4个数字,组成了板块图案,同样0表示没方块,1表示有方块。输入保证板块的图案中正好包含4个方块,且4个方块是连在一起的(准确的说,4个方块是四连通的,即给定的板块是俄罗斯方块的标准板块)。
      第20行包含一个1到7之间的整数,表示板块图案最左边开始的时候是在方格图的哪一列中。注意,这里的板块图案指的是16至19行所输入的板块图案,如果板块图案的最左边一列全是0,则它的左边和实际所表示的板块的左边是不一致的(见样例)
    输出格式
      输出15行,每行10个数字,相邻的数字之间用一个空格分隔,表示板块下落后的方格图。注意,你不需要处理最终的消行。
    样例输入
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 1 0 0
    0 0 0 0 0 0 1 0 0 0
    0 0 0 0 0 0 1 0 0 0
    1 1 1 0 0 0 1 1 1 1
    0 0 0 0 1 0 0 0 0 0
    0 0 0 0
    0 1 1 1
    0 0 0 1
    0 0 0 0
    3
    样例输出
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 1 0 0
    0 0 0 0 0 0 1 0 0 0
    0 0 0 0 0 0 1 0 0 0
    1 1 1 1 1 1 1 1 1 1
    0 0 0 0 1 1 0 0 0 0

代码:

#include <iostream>

using namespace std;

const int MAXN = 20;
bool g[MAXN][MAXN], patt[MAXN][MAXN];

int main(){
    int c;

    for(int i = 1; i <= 15; i ++)
        for(int j = 1; j <= 10; j ++)
            cin >> g[i][j];
    for(int i = 1; i <= 4; i ++)
        for(int j = 1; j <= 4; j ++)
            cin >> patt[i][j];
    cin >> c;
    c -= 1;

    int pos = 0;
    bool op = true;
    while(op){
    	//物体往下移动一个
        ++ pos;
        //判断是否下降到底或者碰到别的方格了
        for(int i = 1; i <= 4; i ++)
            for(int j = 1; j <= 4; j ++)
                if((patt[i][j] && i + pos > 15) || (patt[i][j] && g[i + pos][j + c]))
                    op = false;
    }
    pos -= 1;
    //更新网格
    for(int i = 1; i <= 4; i ++)
            for(int j = 1; j <= 4; j ++)
                g[i + pos][j + c] |= patt[i][j];
    //输出答案
    for(int i = 1; i <= 15; i ++){
        for(int j = 1; j <= 10; j ++)
            cout << g[i][j] << " ";
        cout << endl;
    }
    return 0;
}

  1. 路径解析
    问题描述
      在操作系统中,数据通常以文件的形式存储在文件系统中。文件系统一般采用层次化的组织形式,由目录(或者文件夹)和文件构成,形成一棵树的形状。文件有内容,用于存储数据。目录是容器,可包含文件或其他目录。同一个目录下的所有文件和目录的名字各不相同,不同目录下可以有名字相同的文件或目录。
      为了指定文件系统中的某个文件,需要用路径来定位。在类 Unix 系统(Linux、Max OS X、FreeBSD等)中,路径由若干部分构成,每个部分是一个目录或者文件的名字,相邻两个部分之间用 / 符号分隔。
      有一个特殊的目录被称为根目录,是整个文件系统形成的这棵树的根节点,用一个单独的 / 符号表示。在操作系统中,有当前目录的概念,表示用户目前正在工作的目录。根据出发点可以把路径分为两类:
      绝对路径:以 / 符号开头,表示从根目录开始构建的路径。
      相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。
      例如,有一个文件系统的结构如下图所示。在这个文件系统中,有根目录 / 和其他普通目录 d1、d2、d3、d4,以及文件 f1、f2、f3、f1、f4。其中,两个 f1 是同名文件,但在不同的目录下。
    在这里插入图片描述
      对于 d4 目录下的 f1 文件,可以用绝对路径 /d2/d4/f1 来指定。如果当前目录是 /d2/d3,这个文件也可以用相对路径 …/d4/f1 来指定,这里 … 表示上一级目录(注意,根目录的上一级目录是它本身)。还有 . 表示本目录,例如 /d1/./f1 指定的就是 /d1/f1。注意,如果有多个连续的 / 出现,其效果等同于一个 /,例如 /d1///f1 指定的也是 /d1/f1。
      本题会给出一些路径,要求对于每个路径,给出正规化以后的形式。一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 … 的绝对路径,且不包含连续多个 / 符号。如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /。若这个路径代表根目录,则正规化操作的结果是 /。若路径为空字符串,则正规化操作的结果是当前目录。
    输入格式
      第一行包含一个整数 P,表示需要进行正规化操作的路径个数。
      第二行包含一个字符串,表示当前目录。
      以下 P 行,每行包含一个字符串,表示需要进行正规化操作的路径。
    输出格式
      共 P 行,每行一个字符串,表示经过正规化操作后的路径,顺序与输入对应。
    样例输入
    7
    /d2/d3
    /d2/d4/f1
    …/d4/f1
    /d1/./f1
    /d1///f1
    /d1/
    ///
    /d1/…/…/d2
    样例输出
    /d2/d4/f1
    /d2/d4/f1
    /d1/f1
    /d1/f1
    /d1
    /
    /d2
    评测用例规模与约定
      1 ≤ P ≤ 10。
      文件和目录的名字只包含大小写字母、数字和小数点 .、减号 - 以及下划线 _。
      不会有文件或目录的名字是 . 或 … ,它们具有题目描述中给出的特殊含义。
      输入的所有路径每个长度不超过 1000 个字符。
      输入的当前目录保证是一个经过正规化操作后的路径。
      对于前 30% 的测试用例,需要正规化的路径的组成部分不包含 . 和 … 。
      对于前 60% 的测试用例,需要正规化的路径都是绝对路径。

代码:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

//从s中得到参数 存储到a中
void fun(vector<string> &a, string &s) {
    for (int i = 0; i < s.length();) {
    	//跳过 /
        while (i < s.length() && s[i] == '/')
            ++i;
        int j = i;
        //得到 文件名
        while (j < s.length() && s[j] != '/')
            ++j;
        if (i == j)
            continue;
        //加入到 a 中
        a.push_back(s.substr(i, j - i));
        i = j;
    }
}
//b为 输入的句子 a为当前路径
void work(vector<string> &a, vector<string> &b) {
    for (string &s : b) {
    	//回退
        if (s == "..") {
            if (!a.empty())
                a.pop_back();
        } else if (s == ".");
        else {
        	//加入a中
            a.push_back(s);
        }
    }
    if (a.empty())
        cout << "/" << endl;
    else {
        for (string &s : a)
            cout << "/" << s;
        cout << endl;
    }
}

int main() {
    vector<string> a;
    string s;
    int n;

    cin >> n;
    cin >> s;

    fun(a, s);
    getchar();
    while (n--) {
        vector<string> b, t;
        getline(cin, s);
        fun(b, s);
        //相对路径的情况
        if (s[0] != '/')
            t = a;
        work(t, b);
    }
    return 0;
}

  1. 游戏
    问题描述
      小明在玩一个电脑游戏,游戏在一个n×m的方格图上进行,小明控制的角色开始的时候站在第一行第一列,目标是前往第n行第m列。
      方格图上有一些方格是始终安全的,有一些在一段时间是危险的,如果小明控制的角色到达一个方格的时候方格是危险的,则小明输掉了游戏,如果小明的角色到达了第n行第m列,则小明过关。第一行第一列和第n行第m列永远都是安全的。
      每个单位时间,小明的角色必须向上下左右四个方向相邻的方格中的一个移动一格。
      经过很多次尝试,小明掌握了方格图的安全和危险的规律:每一个方格出现危险的时间一定是连续的。并且,小明还掌握了每个方格在哪段时间是危险的。
      现在,小明想知道,自己最快经过几个时间单位可以达到第n行第m列过关。
    输入格式
      输入的第一行包含三个整数n, m, t,用一个空格分隔,表示方格图的行数n、列数m,以及方格图中有危险的方格数量。
      接下来t行,每行4个整数r, c, a, b,表示第r行第c列的方格在第a个时刻到第b个时刻之间是危险的,包括a和b。游戏开始时的时刻为0。输入数据保证r和c不同时为1,而且当r为n时c不为m。一个方格只有一段时间是危险的(或者说不会出现两行拥有相同的r和c)。
    输出格式
      输出一个整数,表示小明最快经过几个时间单位可以过关。输入数据保证小明一定可以过关。
    样例输入
    3 3 3
    2 1 1 1
    1 3 2 10
    2 2 2 10
    样例输出
    6
    样例说明
      第2行第1列时刻1是危险的,因此第一步必须走到第1行第2列。
      第二步可以走到第1行第1列,第三步走到第2行第1列,后面经过第3行第1列、第3行第2列到达第3行第3列。
    评测用例规模与约定
      前30%的评测用例满足:0 < n, m ≤ 10,0 ≤ t < 99。
      所有评测用例满足:0 < n, m ≤ 100,0 ≤ t < 9999,1 ≤ r ≤ n,1 ≤ c ≤ m,0 ≤ a ≤ b ≤ 100。

代码:

#include <iostream>
#include <queue>

using namespace std;

const int MAXN = 110;
bool st[MAXN][MAXN][3 * MAXN], vis[MAXN][MAXN][3 * MAXN];
int n, m, t, ans = 0, fx[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
struct node{
    int a, b, c;
};

void bfs(){
    queue<node> Q;
    Q.push({1, 1, 0});
    while(!Q.empty()){
        node cur = Q.front();
        Q.pop();
        for(int i = 0; i < 4; i ++){
            int r = cur.a + fx[i][0];
            int c = cur.b + fx[i][1];
            int t = cur.c + 1;
            if(r >= 1 && r <= n && c >= 1 && c <= m && !st[r][c][t] && !vis[r][c][t]){
                if(r == n && c == m){
                    ans = t;
                    return;
                }
                vis[r][c][t] = true;
                Q.push({r, c, t});
            }
        }
    }
}

int main(){
    int r, c, a, b;
    cin >> n >> m >> t;
    while(t --){
        cin >> r >> c >> a >> b;
        for(int i = a; i <= b; i ++)
            st[r][c][i] = true;
    }

    bfs();
    cout << ans << endl;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值