CCF CSP 认证真题部分题解

CCF CSP 俄罗斯方块

只要一牵涉到游戏,大多都是模拟题(不排除一些游戏杀怪背包题)。这个模拟题有些烦人,就分享一下思路

首先计算出方块能下落的深度,然后看看是方块的那一部分导致它不能再下沉,就用深度减去那个部分再4*4方块中的行号,结果就是4*4方块再游戏界面中的行号。思路应该挺清晰的吧。CCF CSP每次都会有模拟题

#include 
const int N = 15, M = 10, K = 4;
int mp[N + 2][M + 1], tmp[K + 1][K + 1], x[K + 1];
int cal(int col) { // 计算可以沉下去多少行i,减去位置
    for(int i = 1; i <= M; ++i) {
        mp[N + 1][i] = 1;
    } //底
    for (int i = 1; ; ++i) {
        for (int j = K; j >= 1; --j) {
            for(int p = 1; p <= K; ++p) {
                if(mp[i][col - 1 + p] && tmp[x[j]][p]) {
                    //printf("%d %d %d %d\n", i, col - 1 + j, x[j], j);
                    return i - x[j];
                }
            }
        }
    }
    return -1;//不可能执行这一步,我设了墙
}
int main()
{
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= M; ++j) {
            scanf("%d", &mp[i][j]);
        }
    }
    int cur = 0;
    for(int i = 1; i <= K; ++i) {
        for (int j = 1; j <= K; ++j) {
            scanf("%d", &tmp[i][j]);
            if(tmp[i][j] != 0) x[++cur] = i;
        }
    }
    int col;
    scanf("%d", &col);
    for (int r = cal(col), i = 1; i <= K; ++i, ++r) {
        if(r < 1) continue;
        for (int c = col; c < col + K; ++c) {
            mp[r][c] |= tmp[i][c - col + 1];
        }
    }
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= M; ++j) {
            printf("%d%c", mp[i][j], " \n"[j == M]);
        }
    }

    return 0;
}

问题描述
试题编号: 201604-2
试题名称: 俄罗斯方块
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏。
  游戏在一个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


CCF CSP 路径解析

这道题就是模仿Linux的cd 命令。注意一下读入的字符串可能为空(10分),目录或文件名可能包含.***或..***(20分)。其他70分是普通样例。

#include 
#include 
using namespace std;
string work(string cur, string str) {
    if(str == "") return cur;
    int len = str.length(), j; string ans;
    if(str[0] != '/' && cur != "/") ans = cur + '/'; //判断时候为绝对路径
    for (int i = 0; i < len; ++i) {
        string tmp;
        if(ans == "") ans = '/';
        switch(str[i]) {
            case '/':
                if(ans[ans.length() - 1] != '/') ans += '/';
                break;
            default:
                for(; i < len && str[i] != '/'; ++i) tmp += str[i];
                if(tmp == "..") {
                    for(j = ans.length() - 1; ans[j] != '/'; --j);
                    ans = ans.substr(0, j);
                    for(j = ans.length() - 1; ans[j] != '/'; --j);
                    ans = ans.substr(0, j);
                    ans += '/';
                }
                else if(tmp != ".") ans += tmp + '/';
                break;
        }
    }
    ans += '/';
    for (j = ans.length() - 1; j > 0 && ans[j] == '/'; --j);
    ans = ans.substr(0, j + 1);
    return ans;
}
int main()
{
    int n;
    while(cin >> n) {
        string cur, str;
        cin >> cur; cin.get();//测试样例有空串,别用getchar()替换cin.get();
        while(n-- > 0) {
            getline(cin, str);
            cout << work(cur, str) << endl;
        }
    }

    return 0;
}

问题描述
试题编号: 201604-3
试题名称: 路径解析
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  在操作系统中,数据通常以文件的形式存储在文件系统中。文件系统一般采用层次化的组织形式,由目录(或者文件夹)和文件构成,形成一棵树的形状。文件有内容,用于存储数据。目录是容器,可包含文件或其他目录。同一个目录下的所有文件和目录的名字各不相同,不同目录下可以有名字相同的文件或目录。
  为了指定文件系统中的某个文件,需要用 路径来定位。在类 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% 的测试用例,需要正规化的路径都是绝对路径。



CCF CSP 游戏

这题肯定宽搜啦,为了不重复搜索某一相同时间地点状态(需要一个数组记录x, y坐标位置和时间)。109毫秒,我再试了下A*启发式寻路算法62毫秒。

先列出启发式搜索的代码清代

#include 
#include 
#include 
#include 
using namespace std;
const int N = 107, inf = 0x3f3f3f3f;
struct Node {
    int a, b;
    Node (int a = inf, int b = 0):a(a), b(b){}
};
struct State {
    int f, g, h, x, y, t;
    State(int x, int y, int f, int g, int h, int t):x(x), y(y), f(f), g(g), h(h), t(t){}
    bool operator < (const State tmp) const {
        return f == tmp.f ? g > tmp.g : f > tmp.f;
    }
};
int n, m;
int get_h(int x, int y) {
    return n - x + m - y;
}
vector  > v;
void init() {
    v.clear(); v.resize(n + 1);
    for (int i = 1; i<= n; ++i) {
        v[i].resize(m + 1);
    }
}
int dir[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1};
bool vis[N][N][N * N];
int bfs(int x, int y) {
    if(x == n && y == m) return 0;
    priority_queue pq;
    int g = 0, h = get_h(x, y), f = g + h;
    State st = State(x, y, f, g, h, 0);
    pq.push(st);
    memset(vis, 0, sizeof vis);
    vis[x][y][0] = 1;
    while(!pq.empty()) {
        st = pq.top(); pq.pop();
        for(int i = 0; i< 4; ++i) {
            x = st.x + dir[i][0], y = st.y + dir[i][1];
            if(x == n && y == m) return st.t + 1;
            if(1 <= x && x <= n && 1 <= y && y <= m && !vis[x][y][st.t + 1] &&
            (st.t + 1 < v[x][y].a || st.t + 1 > v[x][y].b)) {
                vis[x][y][st.t + 1] = 1;
                g = st.g + 1, h = get_h(x, y), f = g + h;
                pq.push(State(x, y, f, g, h, st.t + 1));
            }
        }
    }
    return inf;
}
int main()
{
    int t;
    while(~scanf("%d%d%d", &n, &m, &t)) {
        init();
        int x, y, a, b;
        while(t-- > 0) {
            scanf("%d%d%d%d", &x, &y, &a, &b);
            v[x][y] = Node(a, b);
        }
        printf("%d\n", bfs(1, 1));
    }

    return 0;
}

CCF CSP 再给出一般BFS.

#include 
#include 
#include 
#include 
using namespace std;
const int N = 107, inf = 0x3f3f3f3f;
struct Node {
    int a, b;
    Node (int a = inf, int b = 0):a(a), b(b){}
};
struct State {
    int x, y, t;
    State(int x, int y, int t):x(x), y(y), t(t){}
};
int n, m;
vector  > v;
void init() {
    v.clear(); v.resize(n + 1);
    for (int i = 1; i<= n; ++i) {
        v[i].resize(m + 1);
    }
}
int dir[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1};
bool vis[N][N][N * N];
int bfs(int x, int y) {
    if(x == n && y == m) return 0;
    queue q;
    State st = State(x, y, 0);
    q.push(st);
    memset(vis, 0, sizeof vis);
    vis[x][y][0] = 1;
    while(!q.empty()) {
        st = q.front(); q.pop();
        for(int i = 0; i< 4; ++i) {
            x = st.x + dir[i][0], y = st.y + dir[i][1];
            if(x == n && y == m) return st.t + 1;
            if(1 <= x && x <= n && 1 <= y && y <= m && !vis[x][y][st.t + 1] &&
            (st.t + 1 < v[x][y].a || st.t + 1 > v[x][y].b)) {
                vis[x][y][st.t + 1] = 1;
                q.push(State(x, y, st.t + 1));
            }
        }
    }
    return inf;
}
int main()
{
    int t;
    while(~scanf("%d%d%d", &n, &m, &t)) {
        init();
        int x, y, a, b;
        while(t-- > 0) {
            scanf("%d%d%d%d", &x, &y, &a, &b);
            v[x][y] = Node(a, b);
        }
        printf("%d\n", bfs(1, 1));
    }

    return 0;
}

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


CCF CSP 画图.

有个测试样例是画线时碰到了+(没有考虑这个,会扣10分)。这个代码画图也挺好看的。那个笛卡尔坐标转换为矩阵,只要在逻辑理解上转换就可以了。

#include 
#include 
#include 
using namespace std;
const int N = 107;
char mp[N][N];
int dir[4][2] = {-1, 0, 1, 0, 0, -1, 0, 1}, n, m;
void dfs(int x, int y, char ch) {
    mp[x][y] = ch;
    for (int i= 0; i < 4; ++i) {
        int xx =x + dir[i][0], yy = y + dir[i][1];
        if(0 <= xx && xx < n && 0 <= yy && yy < m && mp[xx][yy] != ch &&
        mp[xx][yy] != '-' && mp[xx][yy] != '|' && mp[xx][yy] != '+') {
            dfs(xx, yy, ch);
        }
    }
}
int main()
{
    int q;
    while(~scanf("%d%d%d", &n, &m, &q)) {
        memset(mp, '.', sizeof mp);
        while(q-- > 0) {
            int opt, x0, y0, x1, y1; char ch;
            scanf("%d", &opt);
            if(opt == 0) {
                scanf("%d%d%d%d", &x0, &y0, &x1, &y1);
                if(x0 == x1) {
                    if(y0 > y1) swap(y0, y1);
                    for (int i = y0; i <= y1; ++i) {
                        if(mp[x0][i] == '-' || mp[x0][i] == '+') mp[x0][i] = '+';
                        else mp[x0][i] = '|';
                    }
                }
                else if(y0 == y1) {
                    if(x0 > x1) swap(x0, x1);
                    for (int i =  x0; i <= x1; ++i) {
                        if(mp[i][y0] == '|' || mp[i][y0] == '+') mp[i][y0] = '+';
                        else mp[i][y0] = '-';
                    }
                }
            }
            else if(opt == 1) {
                scanf("%d%d %c", &x0, &y0, &ch);
                dfs(x0, y0, ch);
            }
        }
        for (int j = m - 1; j >= 0; --j) {
            for (int i = 0; i < n; ++i) {
                putchar(mp[i][j]);
            }
            puts("");
        }
    }

    return 0;
}

问题描述
试题编号: 201512-3
试题名称: 画图
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ASCII Art 的艺术。例如,下图是用 ASCII 字符画出来的 CSPRO 字样。
  ..____.____..____..____...___..
  ./.___/.___||.._.\|.._.\./._.\.
  |.|...\___.\|.|_).|.|_).|.|.|.|
  |.|___.___).|..__/|.._.<|.|_|.|
  .\____|____/|_|...|_|.\_\\___/.
  本题要求编程实现一个用 ASCII 字符来画图的程序,支持以下两种操作:
  Ÿ 画线:给出两个端点的坐标,画一条连接这两个端点的线段。简便起见题目保证要画的每条线段都是水平或者竖直的。水平线段用字符 - 来画,竖直线段用字符 | 来画。如果一条水平线段和一条竖直线段在某个位置相交,则相交位置用字符 + 代替。
  Ÿ 填充:给出填充的起始位置坐标和需要填充的字符,从起始位置开始,用该字符填充相邻位置,直到遇到画布边缘或已经画好的线段。注意这里的相邻位置只需要考虑上下左右 4 个方向,如下图所示,字符 @ 只和 4 个字符 * 相邻。
  .*.
  *@*
  .*.
输入格式
  第1行有三个整数 mnqmn分别表示画布的宽度和高度,以字符为单位。 q表示画图操作的个数。
  第2行至第 q + 1行,每行是以下两种形式之一:
  Ÿ 0  x 1  y 1  x 2  y 2:表示画线段的操作,( x 1y 1)和( x 2y 2)分别是线段的两端,满足要么 x 1 =  x 2 且 y 1 ≠ y 2,要么  y 1 =  y 2 且  x 1≠  x 2
  Ÿ 1  x y c:表示填充操作,( xy)是起始位置,保证不会落在任何已有的线段上; c 为填充字符,是大小写字母。
  画布的左下角是坐标为 (0, 0) 的位置,向右为 x坐标增大的方向,向上为 y坐标增大的方向。这 q个操作按照数据给出的顺序依次执行。画布最初时所有位置都是字符 .(小数点)。
输出格式
  输出有 n行,每行 m个字符,表示依次执行这 q个操作后得到的画图结果。
样例输入
4 2 3
1 0 0 B
0 1 0 2 0
1 0 0 A
样例输出
AAAA
A--A
样例输入
16 13 9
0 3 1 12 1
0 12 1 12 3
0 12 3 6 3
0 6 3 6 9
0 6 9 12 9
0 12 9 12 11
0 12 11 3 11
0 3 11 3 1
1 4 2 C
样例输出
................
...+--------+...
...|CCCCCCCC|...
...|CC+-----+...
...|CC|.........
...|CC|.........
...|CC|.........
...|CC|.........
...|CC|.........
...|CC+-----+...
...|CCCCCCCC|...
...+--------+...
................
评测用例规模与约定
  所有的评测用例满足:2 ≤  mn ≤ 100,0 ≤  q ≤ 100,0 ≤  x <  mx表示输入数据中所有位置的 x坐标),0 ≤  y <  ny表示输入数据中所有位置的 y坐标)。

CCF CSP 最大矩形

枚举每个柱为最小高的情况,向两边拓展,记录最值。最坏情况:1000个柱全相等,人眼一看O(1)出结果,可是我这个算法需要O(n^2)。虽然最多计算百万次级,但是怕CCF CSP的测评机太慢。

#include 
#include 
const int N = 1007;
int a[N]; 
int main()
{
	int n;
	while(~scanf("%d", &n)) {
		for (int i = 1; i <= n; ++i) {
			scanf("%d", a + i);
		}
		a[0] = a[n + 1] = 0;
		int maxV = 0;
		for (int i = 1; i <= n; ++i) {
			int l, r;
			for (l = i - 1; a[l] >= a[i]; --l);
			for (r = i + 1; a[r] >= a[i]; ++r);
			maxV = std::max(maxV, (r -l - 1) * a[i]);
		}
		printf("%d\n", maxV);
	}

	return 0;
}
问题描述
试题编号: 201312-3
试题名称: 最大的矩形
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
  在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i(1 ≤ i ≤ n)个矩形的高度是h i。这n个矩形构成了一个直方图。例如,下图中六个矩形的高度就分别是3, 1, 6, 5, 2, 3。



  请找出能放在给定直方图里面积最大的矩形,它的边要与坐标轴平行。对于上面给出的例子,最大矩形如下图所示的阴影部分,面积是10。
输入格式
  第一行包含一个整数n,即矩形的数量(1 ≤ n ≤ 1000)。
  第二行包含n 个整数h 1, h 2, … , h n,相邻的数之间由空格分隔。(1 ≤ h i ≤ 10000)。h i是第i个矩形的高度。
输出格式
  输出一行,包含一个整数,即给定直方图内的最大矩形的面积。
样例输入
6
3 1 6 5 2 3
样例输出
10




你可能感兴趣的:(模拟题,认证考试,CCF,CSP认证)