第七次CCF计算机软件能力认证

第一题: 折点计数

给定 n 个整数表示一个商店连续 n 天的销售量。

如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。

其他的天都不是折点。

如下图中,第 3 天和第 6 天是折点。

第七次CCF计算机软件能力认证_第1张图片

给定 n 个整数 a1,a2,…,an 表示销售量,请计算出这些天总共有多少个折点。

为了减少歧义,我们给定的数据保证:在这 n 天中相邻两天的销售量总是不同的,即 ai−1≠ai。

注意,如果两天不相邻,销售量可能相同。

输入格式

输入的第一行包含一个整数 n。

第二行包含 n 个整数,用空格分隔,分别表示 a1,a2,…,an。

输出格式

输出一个整数,表示折点出现的数量。

数据范围

所有评测用例满足:1≤n≤1000,每天的销售量是不超过 10000 的非负整数。

输入样例:

7
5 4 1 2 3 6 4

输出样例:

2

 

 解题思路:求出一个点

满足

1、该点小于两边

2、该点大于两边

符合其中之一即可

以下是代码

c++

#include

using namespace std;

const int N = 1010;
int n;
int a[N];

int main()
{
    cin >> n;
    for(int i = 0;i < n;i ++)
        cin >> a[i];
        
    int res = 0;
    for(int i = 1;i < n - 1;i ++)
    {
        if(a[i] > a[i - 1] && a[i] > a[i + 1]) res ++;
        else if(a[i] < a[i - 1] && a[i] < a[i + 1]) res ++;
    }
    cout << res << endl;
    return 0;
}

Python

n = int(input())
l = list(map(int , input().split()))
res = [i for i in range(1 , n - 1) if (l[i] > l[i - 1] and l[i] > l[i + 1]) or (l[i] < l[i - 1] and l[i] < l[i + 1])]
print(len(res))

第二题: 俄罗斯方块

俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏。

游戏在一个 15 行 10 列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块。

每一轮,都会有一个新的由 4 个小方块组成的板块从方格图的上方落下,玩家可以操作板块左右移动放到合适的位置,当板块中某一个方块的下边缘与方格图上的方块上边缘重合或者达到下边界时,板块不再移动,如果此时方格图的某一行全放满了方块,则该行被消除并得分。

在这个问题中,你需要写一个程序来模拟板块下落,你不需要处理玩家的操作,也不需要处理消行和得分。

具体的,给定一个初始的方格图,以及一个板块的形状和它下落的初始位置,你要给出最终的方格图。

输入格式

输入的前 15 行包含初始的方格图,每行包含 10 个数字,相邻的数字用空格分隔。如果一个数字是 0,表示对应的方格中没有方块,如果数字是 1,则表示初始的时候有方块。输入保证前 4 行中的数字都是 0。

输入的第 16 至第 19 行包含新加入的板块的形状,每行包含 44个数字,组成了板块图案,同样 0 表示没方块,1 表示有方块。输入保证板块的图案中正好包含 4 个方块,且 4 个方块是连在一起的(准确的说,4 个方块是四连通的,即给定的板块是俄罗斯方块的标准板块)。

第 20 行包含一个 1 到 7 之间的整数,表示板块图案最左边开始的时候是在方格图的哪一列中。注意,这里的板块图案指的是 16 至 19 行所输入的板块图案,如果板块图案的最左边一列全是 0,则它的左边和实际所表示的板块的左边是不一致的(见样例)。

方格图共 10 列,从左到右依次为第 1∼10 列。

输出格式

输出 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

样例解释

注意,本样例中,给定的 4×4 板块图案的最左边位于方格图的第 3 列。

实际板块的最左端其实位于方格图的第 4 列。

 解题思路:枚举每一行,如果这一行和我们的图形有重合,那么该图形一定放置的是前一行,为了防止越界的情况,那么多一行0即可解决溢出的问题。

以下是代码

c++

#include
#include

using namespace std;
const int N = 20;

int g[N][N] , s[N][N];
int p[N][N];
int n;

// 判断是否重叠(t时刻重叠,需要更改t-1时刻)
bool draw(int x , int y)
{
    memcpy(s , g , sizeof s);
    for(int i = 0;i < 4;i ++)
        for(int j = 0;j < 4;j ++)
            if(p[i][j])
            {
                int a = x + i , b = y + j;
                s[a][b] ++;
                // 有重叠
                if(s[a][b] == 2) return true;
            }
    return false;
}

int main()
{
    for(int i = 0;i < 15;i ++)
        for(int j = 0;j < 10;j ++)
            cin >> g[i][j];
    
    // 新加一个地板防止长条越界
    for(int i = 0;i < 10;i ++) g[15][i] = 1;
    
    for(int i = 0;i < 4;i ++)
        for(int j = 0;j < 4;j ++)
            cin >> p[i][j];
    
    cin >> n;
    // 下标从0开始
    n --;
    
    // 枚举时刻
    for(int i = 0;;i ++)
        if(draw(i , n))
        {
            // 处理i - 1时刻
            draw(i - 1 , n);
            break;
        }
    
    for(int i = 0;i < 15;i ++)
    {
        for(int j = 0;j < 10;j ++)
            cout << s[i][j] << " ";
        cout << endl;
    }
        
}

第三题:路径解析

在操作系统中,数据通常以文件的形式存储在文件系统中。

文件系统一般采用层次化的组织形式,由目录(或者文件夹)和文件构成,形成一棵树的形状。

文件有内容,用于存储数据。

目录是容器,可包含文件或其他目录。

同一个目录下的所有文件和目录的名字各不相同,不同目录下可以有名字相同的文件或目录。

为了指定文件系统中的某个文件,需要用路径来定位。

在类 Unix 系统(Linux、Max OS X、FreeBSD等)中,路径由若干部分构成,每个部分是一个目录或者文件的名字,相邻两个部分之间用 / 符号分隔。

有一个特殊的目录被称为根目录,是整个文件系统形成的这棵树的根节点,用一个单独的 / 符号表示。

在操作系统中,有当前目录的概念,表示用户目前正在工作的目录。根据出发点可以把路径分为两类:

  • 绝对路径:以 / 符号开头,表示从根目录开始构建的路径。
  • 相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。

例如,有一个文件系统的结构如下图所示。

在这个文件系统中,有根目录 / 和其他普通目录 d1、d2、d3、d4,以及文件 f1、f2、f3、f1、f4。

其中,两个 f1 是同名文件,但在不同的目录下。

第七次CCF计算机软件能力认证_第2张图片

对于 d4 目录下的 f1 文件,可以用绝对路径 /d2/d4/f1 来指定。

如果当前目录是 /d2/d3,这个文件也可以用相对路径 ../d4/f1 来指定,这里 .. 表示上一级目录(注意,根目录的上一级目录是它本身)。

还有 . 表示本目录,例如 /d1/./f1 指定的就是 /d1/f1

注意,如果有多个连续的 / 出现,其效果等同于一个 /,例如 /d1///f1 指定的也是 /d1/f1

本题会给出一些路径,要求对于每个路径,给出正规化以后的形式。

一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 .. 的绝对路径,且不包含连续多个 / 符号。

如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /

若这个路径代表根目录,则正规化操作的结果是 /

若路径为空字符串,则正规化操作的结果是当前目录。

输入格式

第一行包含一个整数 P,表示需要进行正规化操作的路径个数。

第二行包含一个字符串,表示当前目录。

以下 P 行,每行包含一个字符串,表示需要进行正规化操作的路径。

输出格式

共 P 行,每行一个字符串,表示经过正规化操作后的路径,顺序与输入对应。

数据范围

1≤P≤10。
文件和目录的名字只包含大小写字母、数字和小数点 .、减号 - 以及下划线 _
不会有文件或目录的名字是 . 或 ..,它们具有题目描述中给出的特殊含义。
输入的所有路径每个长度不超过 1000 个字符。
输入的当前目录保证是一个经过正规化操作后的路径。
对于前 30% 的测试用例,需要正规化的路径的组成部分不包含 . 和 ..
对于前 60% 的测试用例,需要正规化的路径都是绝对路径。

输入样例:

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、../ 表示的是上一级
2、./ 表示的是当前目录
3、否则 要将其添加到路径中 得到的路径为cur

1、  (1) 如果需要修订的路径是相对路径 需要考虑第一行给定的当前路径 cur
        (2)如果需要修订的路径是绝对路径 就不用考虑第一行给定的当前路径 ab
        (3)用vector 存放那些不需要的/ 路径
2.     (1)若当前cur 为空 则输出根路径即可
        (2)否则 依次输出当前cur里的路径

以下是代码

c++

#include
#include
#include

using namespace std;

vector get(string str)
{
    vector res;

    for(int i = 0;i < str.size();i ++)
    {
        if(str[i] == '/') continue;
        int j = i + 1;
        while(j < str.size() && str[j] != '/') j ++;
        res.push_back(str.substr(i , j - i));
        i = j;
    }
    return res;
}

void check(vectorcur , vectorpath)
{
    for(auto ch : path)
    {
        // . 表示本目录
        if(ch == ".") continue;
        else if(ch == "..")
        {
            // .. 表示上一级目录 需要进行弹出最后一项
            if(cur.size()) cur.pop_back();
        }
        else cur.push_back(ch);
    }
    
    if(cur.empty())
    {
        puts("/");
        return ;
    }
    for(auto i : cur)
        cout << "/" << i;
    cout << endl;
}

int main()
{
    int n;
    // 当前目录
    string str;
    cin >> n >> str;
    getchar();
    // cur表示的是相对路径  ab是绝对路径
    vectorcur = get(str) , ab;
    
    while(n --)
    {
        string t;
        // 注意输入有空行
        getline(cin , t);
        // 代转化路径
        vectorpath = get(t);
        
        // 绝对路径:以 / 符号开头,表示从根目录开始构建的路径
        if(t.size() && t[0] == '/') check(ab , path);
        // 相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。
        else check(cur , path);
    }
}

第四题:游戏

小明在玩一个电脑游戏,游戏在一个 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)。

输出格式

输出一个整数,表示小明最快经过几个时间单位可以过关。

输入数据保证小明一定可以过关。

数据范围

前 30% 的评测用例满足:0 所有评测用例满足:0

输入样例:

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 列

解题思路:类似dp的思想使用两维的空间存储图的坐标,第三维存储不同时间下这个位置的状态,然后使用bfs进行遍历,求出到达最终位置的时间。

以下是代码

c++

#include
#include
#include

using namespace std;

typedef pair PII;
const int N = 350;
int dist[N][N][N];
bool g[N][N][N];
int dx[] = {0 , 0 , 1 , -1};
int dy[] = {1 , -1 , 0 , 0};

int n , m , t;

int bfs()
{
    queue>q;
    q.push({0 , {1 , 1}});
    
    while(!q.empty())
    {
        auto t = q.front();
        q.pop();
        
        for(int i = 0;i < 4;i ++)
        {
            int x = t.second.first + dx[i] , y = t.second.second + dy[i];
            int ti = t.first;
            
            if(x <= 0 || y <= 0 || x > n || y > m) continue;
            if(g[x][y][ti + 1]) continue;
            if(dist[x][y][ti + 1] > ti + 1)
            {
                if(x == n && y == m) return ti + 1;
                
                dist[x][y][ti + 1] = ti + 1;
                q.push({ti + 1 , {x , y}});
            }
        }
    }
}

int main()
{
    memset(g , 0 , sizeof g);
    memset(dist , 0x3f , sizeof dist);
    cin >> n >> m >> t;
    while(t --)
    {
        int r , c , a , b;
        cin >> r >> c >> a >> b;
        for(int i = a;i <= b;i ++)
            g[r][c][i] = true;
    }
    
    cout << bfs() << endl;
    return 0;
}

第五题:网络连接

此题应该使用的是连通性dp,集合的最小表示方法(不会)

#include 
#include 
#include 
#include 

using namespace std;

const int N = 510, M = 1 << 6, H = 1e6, INF = 0x3f3f3f3f;

int n, m, p;
int w[N][6], es[N], ec[N][M];
int f[2][H], fs[2][H], ft[2];
char g[N];
int fa[7];
int fstate[224695][M];

int get(int state, int k)
{
    return state >> k * 3 & 7;
}

void update(int i, int state, int cost)
{
    if (state == -1) return;
    if (f[i][state] != -1) f[i][state] = min(f[i][state], cost);
    else
    {
        f[i][state] = cost;
        fs[i][ft[i] ++ ] = state;
    }
}

void clear(int k)
{
    for (int i = 0; i < ft[k]; i ++ )
        f[k][fs[k][i]] = -1;
    ft[k] = 0;
}

int find(int x)
{
    if (fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}

int get_state(int state, int k)  // 给最小表示法state增加边集k
{
    if (k == 1) k = 0;
    if (fstate[state][k / 2] >= -1) return fstate[state][k / 2];
    for (int i = 0; i <= p; i ++ )
        if (get(state, i) || (k >> i & 1))
            fa[i] = i;
        else fa[i] = -1;
    for (int i = 0; i <= p; i ++ )
        if (get(state, i))
            for (int j = i + 1; j <= p; j ++ )
                if (get(state, j) == get(state, i))
                    fa[find(j)] = find(i);

    for (int i = 0; i <= p; i ++ )
        if (k >> i & 1)
        {
            for (int j = i + 1; j <= p; j ++ )
                if (k >> j & 1)
                    fa[find(j)] = find(i);
            break;
        }

    if (fa[0] != -1)  // 第一个点要么没被选,要么所在集合大小至少为2
    {
        bool flag = false;
        for (int i = 1; i <= p; i ++ )
            if (fa[i] != -1 && find(i) == find(0))
            {
                flag = true;
                break;
            }
        if (!flag) return fstate[state][k / 2] = -1;
    }

    int res = 0;
    for (int i = 1, cnt = 1; i <= p; i ++ )
        if (!get(res, i - 1) && fa[i] != -1)
        {
            res += cnt << (i - 1) * 3;
            for (int j = i + 1; j <= p; j ++ )
                if (!get(res, j - 1) && fa[j] != -1 && find(j) == find(i))
                    res += cnt << (j - 1) * 3;
            cnt ++ ;
        }
    return fstate[state][k / 2] = res;
}

int main()
{
    memset(fstate, -0x3f, sizeof fstate);
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d%d%d", &n, &m, &p);
        memset(w, -1, sizeof w);
        scanf("%s", g + 1);
        while (m -- )
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            w[a][b - a - 1] = c;
        }
        memset(es, 0, sizeof es);
        memset(ec, 0, sizeof ec);
        for (int i = 1; i <= n; i ++ )
        {
            for (int j = 0; j < p; j ++ )
                if (w[i][j] != -1)
                {
                    es[i] += 1 << j;
                }
            for (int j = 0; j < 1 << p; j ++ )
                if ((j & es[i]) == j)
                    for (int k = 0; k < p; k ++ )
                        if (j >> k & 1)
                            ec[i][j] += w[i][k];
        }
        memset(f, -1, sizeof f);
        update(1, 0, 0);
        int res = INF, maxp;
        for (int i = 1; i <= n; i ++ )
            if (g[i] == '1')
                maxp = i;
        for (int i = 1; i <= n; i ++ )
        {
            clear(i + 1 & 1);
            for (int j = 0; j < ft[i & 1]; j ++ )
            {
                int state = fs[i & 1][j], cost = f[i & 1][state];
                for (int k = 0; k < 1 << p; k ++ )
                {
                    if ((k & es[i]) != k) continue;
                    if (g[i] == '1' && !get(state, 0) && !k) continue;
                    update(i + 1 & 1, get_state(state, k * 2 + 1), cost + ec[i][k]);
                }
                if (i >= maxp && state == 1) res = min(res, cost);
            }
        }
        printf("%d\n", res);
    }

    return 0;
}

 

你可能感兴趣的:(ccf,csp,算法,数据结构)