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

第一题:数位之和

给定一个十进制整数 n,输出 n 的各位数字之和。

输入格式

输入一个整数 n。

输出格式

输出一个整数,表示答案。

数据范围

1≤n≤1e9

输入样例:
20151220
输出样例:
13
样例解释

20151220 的各位数字之和为 2+0+1+5+1+2+2+0=13。

解题思路: 简单枚举每一位相加即可

以下是代码

C++

#include<iostream>
#include<algorithm>

using namespace std;

string s;

int main()
{
    cin >> s;
    int res = 0;
    for(int i = 0;i < s.size();i ++)
        res += s[i] - '0';
    cout << res << endl;
    return 0;
}

Python

n = int(input())
res = 0
for i in str(n):
    res += ord(i) - ord('0')
print(res)

第二题:消除类游戏

消除类游戏是深受大众欢迎的一种游戏,游戏在一个包含有 n 行 m 列的游戏棋盘上进行,棋盘的每一行每一列的方格上放着一个有颜色的棋子,当一行或一列上有连续三个或更多的相同颜色的棋子时,这些棋子都被消除。

当有多处可以被消除时,这些地方的棋子将同时被消除。

现在给你一个 n 行 m 列的棋盘,棋盘中的每一个方格上有一个棋子,请给出经过一次消除后的棋盘。

请注意:一个棋子可能在某一行和某一列同时被消除。

输入格式

输入的第一行包含两个整数 n,m,用空格分隔,分别表示棋盘的行数和列数。

接下来 n 行,每行 m 个整数,用空格分隔,分别表示每一个方格中的棋子的颜色。

颜色使用 1 至 9 编号。

输出格式

输出 n 行,每行 m 个整数,相邻的整数之间使用一个空格分隔,表示经过一次消除后的棋盘。

如果一个方格中的棋子被消除,则对应的方格输出 0,否则输出棋子的颜色编号。

数据范围

所有的评测用例满足:1≤n,m≤30。

输入样例1:
4 5
2 2 3 1 2
3 4 5 1 4
2 3 2 1 3
2 2 2 4 4
输出样例1:
2 2 3 0 2
3 4 5 0 4
2 3 2 0 3
0 0 0 4 4
样例1解释

棋盘中第 4 列的 1 和第 4 行的 2 可以被消除,其他的方格中的棋子均保留。

输入样例2:
4 5
2 2 3 1 2
3 1 1 1 1
2 3 2 1 3
2 2 3 3 3
输出样例2:
2 2 3 0 2
3 0 0 0 0
2 3 2 0 3
2 2 0 0 0
样例2解释

棋盘中所有的 1 以及最后一行的 3 可以被同时消除,其他的方格中的棋子均保留。

 解题思路:题目描述一行或一列上有连续三个或更多的棋子时,会被消除,因此我们可以遍历每一个点检查是否有连续三个相连,如果有那么,将这个点置成0即可

以下是代码

C++

#include<iostream>
#include<cstring>

using namespace std;

const int N = 50;
int n , m;
int g[N][N];
bool st[N][N];

int main()
{
    memset(st , 0 , sizeof st);
    cin >> n >> m;
    for(int i = 0;i < n;i ++)
        for(int j = 0;j < m;j ++)
            cin >> g[i][j];
    
    for(int i = 0;i < n;i ++)
        for(int j = 0;j < m;j ++)
        {
            if(j + 2 < m && g[i][j] == g[i][j + 1] && g[i][j + 1] == g[i][j + 2])
                st[i][j] = st[i][j + 1] = st[i][j + 2] = 1;
            
            if(i + 2 < n && g[i][j] == g[i + 1][j] && g[i + 2][j] == g[i + 1][j])
                st[i][j] = st[i + 1][j] = st[i + 2][j] = 1;
        }
    
    for(int i = 0;i < n;i ++)
    {
        for(int j = 0;j < m;j ++)
            if(st[i][j]) cout << "0 ";
            else cout << g[i][j] << " ";
        cout << endl;
    }
    return 0;
}

第三题:画图

用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ASCII Art 的艺术。

例如,下图是用 ASCII 字符画出来的 CSPRO 字样。

  ..____.____..____..____...___..
  ./.___/.___||.._.\|.._.\./._.\.
  |.|...\___.\|.|_).|.|_).|.|.|.|
  |.|___.___).|..__/|.._.<|.|_|.|
  .\____|____/|_|...|_|.\_\\___/.

本题要求编程实现一个用 ASCII 字符来画图的程序,支持以下两种操作:

  • 画线:给出两个端点的坐标,画一条连接这两个端点的线段。简便起见题目保证要画的每条线段都是水平或者竖直的。水平线段用字符 - 来画,竖直线段用字符 | 来画。如果一条水平线段和一条竖直线段在某个位置相交,则相交位置用字符 + 代替。
  • 填充:给出填充的起始位置坐标和需要填充的字符,从起始位置开始,用该字符填充相邻位置,直到遇到画布边缘或已经画好的线段。注意这里的相邻位置只需要考虑上下左右 4 个方向,如下图所示,字符 @ 只和 4 个字符 * 相邻。
  .*.
  *@*
  .*.
输入格式

第 1 行有三个整数 m,n 和 q。m 和 n 分别表示画布的宽度和高度,以字符为单位。q 表示画图操作的个数。

第 2 行至第 q+1 行,每行是以下两种形式之一:

  • 0 x1 y1 x2 y2:表示画线段的操作,(x1,y1) 和 (x2,y2) 分别是线段的两端,满足要么 x1=x2 且 y1≠y2,要么 y1=y2 且 x1≠x2。
  • 1 x y c:表示填充操作,(x,y) 是起始位置,保证不会落在任何已有的线段上;c 为填充字符,是大小写字母。

画布的左下角是坐标为 (0,0) 的位置,向右为 x 坐标增大的方向,向上为 y 坐标增大的方向。

这 q 个操作按照数据给出的顺序依次执行。画布最初时所有位置都是字符 .(小数点)。

输出格式

输出有 n 行,每行 m 个字符,表示依次执行这 q 个操作后得到的画图结果。

数据范围

2≤m,n≤100,
0≤q≤100,
0≤x<m(x 表示输入数据中所有位置的 x 坐标),
0≤y<n(y 表示输入数据中所有位置的 y 坐标)。

输入样例1:
4 2 3
1 0 0 B
0 1 0 2 0
1 0 0 A
输出样例1:
AAAA
A--A
输入样例2:
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
输出样例2:
................
...+--------+...
...|CCCCCCCC|...
...|CC+-----+...
...|CC|.........
...|CC|.........
...|CC|.........
...|CC|.........
...|CC|.........
...|CC+-----+...
...|CCCCCCCC|...
...+--------+...
................

解题思路:

对于填充操作,使用深度优先遍历进行图画

对于画线操作,直接遍历的方法进行遍历

坑点:注意这里的坐标和数组中使用的坐标是相反的,仅仅只需要倒着读入就行

以下是代码

C++

#include<iostream>
#include<cstring>

using namespace std;

const int N = 110;
int n , m , q;
char g[N][N];
bool st[N][N];

void dfs(int x , int y , char ch)
{
    if(x < 0 || x >= n || y < 0 || y >= m) return ;
    if(st[x][y] || g[x][y] == '-' || g[x][y] == '|' || g[x][y] == '+') return ;
    st[x][y] = true;
    g[x][y] = ch;
    
    dfs(x + 1 , y , ch);
    dfs(x - 1 , y , ch);
    dfs(x , y + 1 , ch);
    dfs(x , y - 1 , ch);
}

void change(int x1 , int y1 , int x2 , int y2)
{
    if(x1 == x2)
    {// 同一行
        for(int i = y1;i <= y2;i ++)
        {
            if(g[x1][i] == '|' || g[x1][i] == '+') g[x1][i] = '+';
            else g[x1][i] = '-';
        }
    }
    // 同一列
    else if (y1 == y2)
    {
        for(int i = x1;i <= x2;i ++)
        {
            if(g[i][y1] == '-' || g[i][y1] == '+') g[i][y1] = '+';
            else g[i][y1] = '|';
        }
    }
}

int main()
{
    cin >> m >> n >> q;
    for(int i = 0;i < n;i ++)
        for(int j = 0;j < m;j ++)
            g[i][j] = '.';
            
    while(q --)
    {
        // x 宽度
        // y 高度
        int type , x , y;
        cin >> type;
        // 填充操作
        if(type)
        {
            memset(st , 0 , sizeof st);
            char ch;
            cin >> y >> x >> ch;
            dfs(x , y , ch);
        }
        // 画线段的操作
        else
        {
            int y1 , x1;
            cin >> y >> x >> y1 >> x1;
            if(x > x1) swap(x , x1);
            if(y > y1) swap(y , y1);
            change(x , y , x1 , y1);
        }
    }
    
    for(int i = n - 1;i >= 0;i --)
    {
        for(int j = 0;j < m;j ++)
            cout << g[i][j];
        cout << endl;
    }
    return 0;
}

第四题:送货

为了增加公司收入,F 公司新开设了物流业务。

由于 F 公司在业界的良好口碑,物流业务一开通即受到了消费者的欢迎,物流业务马上遍及了城市的每条街道。

然而,F 公司现在只安排了小明一个人负责所有街道的服务。

任务虽然繁重,但是小明有足够的信心,他拿到了城市的地图,准备研究最好的方案。

城市中有 n 个交叉路口,m 条街道连接在这些交叉路口之间,每条街道的首尾都正好连接着一个交叉路口。

除开街道的首尾端点,街道不会在其他位置与其他街道相交。

每个交叉路口都至少连接着一条街道,有的交叉路口可能只连接着一条或两条街道。

小明希望设计一个方案,从编号为 1 的交叉路口出发,每次必须沿街道去往街道另一端的路口,再从新的路口出发去往下一个路口,直到所有的街道都经过了正好一次。

输入格式

输入的第一行包含两个整数 n,m,表示交叉路口的数量和街道的数量,交叉路口从 1 到 n 标号。

接下来 m 行,每行两个整数 a,b(a≠b),表示和标号为 a 的交叉路口和标号为 b 的交叉路口之间有一条街道,街道是双向的,小明可以从任意一端走向另一端。

两个路口之间最多有一条街道。

输出格式

如果小明可以经过每条街道正好一次,则输出一行包含 m+1 个整数 p1,p2,p3,…,pm+1,表示小明经过的路口的顺序,相邻两个整数之间用一个空格分隔。

如果有多种方案满足条件,则输出字典序最小的一种方案,即首先保证 p1 最小,p1 最小的前提下再保证 p2 最小,依此类推。

如果不存在方案使得小明经过每条街道正好一次,则输出一个整数 −1。

数据范围

前 30%30% 的评测用例满足:1≤n≤10,n−1≤m≤20。
前 50%50% 的评测用例满足:1≤n≤100,n−1≤m≤10000。
所有评测用例满足:1≤n≤10000,n−1≤m≤100000。

输入样例1:
4 5
1 2
1 3
1 4
2 4
3 4
输出样例1:
1 2 4 1 3 4
样例1解释

城市的地图和小明的路径如下图所示。

px.png

输入样例2:
4 6
1 2
1 3
1 4
2 4
3 4
2 3
输出样例2:
-1
样例2解释

城市的地图如下图所示,不存在满足条件的路径。

p2.png

解题思路:读题,可以知道需要找一条每一条边只经过一次的路径,这就是欧拉回路的经典模型。

存在欧拉回路的条件

1.每一个节点的度数是偶的

2.有且只有两个点的度数是奇的

对于这个题目:首先需要统计每一个节点的度数,注意题目并没有保证是联通的,因此需要使用并查集进行判断,注意题目需要从1号点出发,因此如果有度数为奇数的点,1号点一定是其中之一。

以下是代码

C++

// 使用并查集检查是否联通
// 使用欧拉回路判断条件进行判断是否有通路
// 使用dfs进行通路的枚举
#include<iostream>
#include<set>

using namespace std;

const int N = 10010 , M = 100010;

// 存边
set<int>se[M];
int res[M] , idx;
int p[M];
int n , m;

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

void dfs(int u)
{
    while(se[u].size())
    {
        int t = *se[u].begin();
        se[u].erase(t) , se[t].erase(u);
        dfs(t);
    }
    res[++ idx] = u;
}

int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)
        p[i] = i;
    while(m --)
    {
        int a , b;
        cin >> a >> b;
        
        se[a].insert(b) , se[b].insert(a);
        if(find(a) != find(b)) p[find(a)] = find(b);
    }
    
    int cnt = 0;
    for(int i = 1;i <= n;i ++)
    {
        if(find(i) != find(1)) 
        {
            puts("-1");
            return 0;
        }
        // 计算奇数度数的点
        else if(se[i].size() % 2) cnt ++;
    }
    
    // 题目需要从1号点出发因此1号点的度数应该是奇数
    if(cnt != 2 && cnt != 0 || cnt == 2 && se[1].size() % 2 == 0)
    {
        puts("-1");
        return 0;
    }
    
    dfs(1);
    
    for(int i = idx;i;i --)
        cout << res[i] << " ";
    return 0;
}

第五题:矩阵

题目很长,题目需要使用矩阵乘法、快速幂、位运算(考试的时候大概率做不出来)直接学习其他人的就行。

以下是代码

C++

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("inline")

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef unsigned int UI;
const int N = 1010, M = 32;

struct Bit
{
    UI a[M];
    void set1(int x)
    {
        a[x >> 5] |= 1u << (x & 31);
    }
    void set0(int x)
    {
        a[x >> 5] &= ~(1u << (x & 31));
    }
    int get(int x)
    {
        return a[x >> 5] >> (x & 31) & 1;
    }
    Bit operator& (const Bit& b) const
    {
        Bit c;
        for (int i = 0; i < M; i ++ )
            c.a[i] = a[i] & b.a[i];
        return c;
    }
    int count_single(UI x)
    {
        x = x ^ (x >> 1);
        x = x ^ (x >> 2);
        x = x ^ (x >> 4);
        x = x ^ (x >> 8);
        x = x ^ (x >> 16);
        return x;
    }
    int count()
    {
        int res = 0;
        for (int i = 0; i < M; i ++ )
            res ^= count_single(a[i]);
        return res & 1;
    }
};

struct Mat
{
    Bit h[N], v[N];
    int n, m;

    Mat operator* (const Mat& b) const
    {
        Mat c;
        c.n = n, c.m = b.m;
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < b.m; j ++ )
                if ((h[i] & b.v[j]).count())
                    c.h[i].set1(j), c.v[j].set1(i);
                else
                    c.h[i].set0(j), c.v[j].set0(i);
        return c;
    }
}b, A[30];
char str[N];

int main()
{
    int m;
    scanf("%d", &m);
    A[0].n = A[0].m = m, b.n = m, b.m = 1;
    for (int i = 0; i < m; i ++ )
    {
        scanf("%s", str);
        for (int j = 0; j < m; j ++ )
            if (str[j] == '1')
                A[0].h[i].set1(j), A[0].v[j].set1(i);
    }
    for (int i = 1; i < 30; i ++ ) A[i] = A[i - 1] * A[i - 1];
    scanf("%s", str);
    for (int i = 0; i < m; i ++ )
        if (str[i] == '1')
            b.h[i].set1(0), b.v[0].set1(i);

    int n;
    scanf("%d", &n);
    while (n -- )
    {
        int k;
        scanf("%d", &k);
        Mat res = b;
        for (int i = 0; i < 30; i ++ )
            if (k >> i & 1)
                res = A[i] * res;
        for (int i = 0; i < m; i ++ )
            printf("%d", res.h[i].get(0));
        puts("");
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值