ccf-csp 2017春季真题题解


  1. 分蛋糕
    问题描述
      小明今天生日,他有n块蛋糕要分给朋友们吃,这n块蛋糕(编号为1到n)的重量分别为a1, a2, …, an。小明想分给每个朋友至少重量为k的蛋糕。小明的朋友们已经排好队准备领蛋糕,对于每个朋友,小明总是先将自己手中编号最小的蛋糕分给他,当这个朋友所分得蛋糕的重量不到k时,再继续将剩下的蛋糕中编号最小的给他,直到小明的蛋糕分完或者这个朋友分到的蛋糕的总重量大于等于k。
      请问当小明的蛋糕分完时,总共有多少个朋友分到了蛋糕。
    输入格式
      输入的第一行包含了两个整数n, k,意义如上所述。
      第二行包含n个正整数,依次表示a1, a2, …, an。
    输出格式
      输出一个整数,表示有多少个朋友分到了蛋糕。
    样例输入
    6 9
    2 6 5 6 3 5
    样例输出
    3
    样例说明
      第一个朋友分到了前3块蛋糕,第二个朋友分到了第4、5块蛋糕,第三个朋友分到了最后一块蛋糕。
    评测用例规模与约定
      对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ k ≤ 10000,1 ≤ ai ≤ 1000。

代码:

#include <iostream>

using namespace std;

int main(){
    int n, m, a, ans = 0, tmp = 0;

    cin >> n >> m;
    while(n --){
        cin >> a;
        tmp += a;
        if(tmp >= m){
            ++ ans;
            tmp = 0;
        }
    }
    if(tmp)
        ++ ans;
    cout << ans << endl;
    return 0;
}

  1. 学生排队
    问题描述
      体育老师小明要将自己班上的学生按顺序排队。他首先让学生按学号从小到大的顺序排成一排,学号小的排在前面,然后进行多次调整。一次调整小明可能让一位同学出队,向前或者向后移动一段距离后再插入队列。
      例如,下面给出了一组移动的例子,例子中学生的人数为8人。
      0)初始队列中学生的学号依次为1, 2, 3, 4, 5, 6, 7, 8;
      1)第一次调整,命令为“3号同学向后移动2”,表示3号同学出队,向后移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 3, 6, 7, 8;
      2)第二次调整,命令为“8号同学向前移动3”,表示8号同学出队,向前移动3名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 8, 3, 6, 7;
      3)第三次调整,命令为“3号同学向前移动2”,表示3号同学出队,向前移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 3, 5, 8, 6, 7。
      小明记录了所有调整的过程,请问,最终从前向后所有学生的学号依次是多少?
      请特别注意,上述移动过程中所涉及的号码指的是学号,而不是在队伍中的位置。在向后移动时,移动的距离不超过对应同学后面的人数,如果向后移动的距离正好等于对应同学后面的人数则该同学会移动到队列的最后面。在向前移动时,移动的距离不超过对应同学前面的人数,如果向前移动的距离正好等于对应同学前面的人数则该同学会移动到队列的最前面。
    输入格式
      输入的第一行包含一个整数n,表示学生的数量,学生的学号由1到n编号。
      第二行包含一个整数m,表示调整的次数。
      接下来m行,每行两个整数p, q,如果q为正,表示学号为p的同学向后移动q,如果q为负,表示学号为p的同学向前移动-q。
    输出格式
      输出一行,包含n个整数,相邻两个整数之间由一个空格分隔,表示最终从前向后所有学生的学号。
    样例输入
    8
    3
    3 2
    8 -3
    3 -2
    样例输出
    1 2 4 3 5 8 6 7
    评测用例规模与约定
      对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 1000,所有移动均合法。

代码:

//使用双射函数求解
#include <iostream>

using namespace std;

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

int main(){
    int n, m;
    int a, b;

    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
        cnt[i] = i, arr[i] = i;
    while(m --){
        cin >> a >> b;
        if(b > 0){
            for(int i = cnt[a]; i < cnt[a] + b; i ++){
                cnt[arr[i + 1]] --;
                arr[i] = arr[i + 1];
            }
        }else{
            for(int i = cnt[a]; i > cnt[a] + b; i --){
                cnt[arr[i - 1]] ++;
                arr[i] = arr[i - 1];
            }
        }
        cnt[a] += b;
        arr[cnt[a]] = a;
    }
    for(int i = 1; i <= n; i ++)
        cout << arr[i] << " ";
    return 0;
}

  1. Markdown
    问题描述
      Markdown 是一种很流行的轻量级标记语言(lightweight markup language),广泛用于撰写带格式的文档。例如以下这段文本就是用 Markdown 的语法写成的:
    在这里插入图片描述
      这些用 Markdown 写成的文本,尽管本身是纯文本格式,然而读者可以很容易地看出它的文档结构。同时,还有很多工具可以自动把 Markdown 文本转换成 HTML 甚至 Word、PDF 等格式,取得更好的排版效果。例如上面这段文本通过转化得到的 HTML 代码如下所示:
    在这里插入图片描述
      本题要求由你来编写一个 Markdown 的转换工具,完成 Markdown 文本到 HTML 代码的转换工作。简化起见,本题定义的 Markdown 语法规则和转换规则描述如下:
      ●区块:区块是文档的顶级结构。本题的 Markdown 语法有 3 种区块格式。在输入中,相邻两个区块之间用一个或多个空行分隔。输出时删除所有分隔区块的空行。
      ○段落:一般情况下,连续多行输入构成一个段落。段落的转换规则是在段落的第一行行首插入 <p>,在最后一行行末插入 </p>
      ○标题:每个标题区块只有一行,由若干个 # 开头,接着一个或多个空格,然后是标题内容,直到行末。# 的个数决定了标题的等级。转换时,# Heading 转换为 <h1>Heading</h1>## Heading 转换为 <h2>Heading</h2>,以此类推。标题等级最深为 6。
      ○无序列表:无序列表由若干行组成,每行由 * 开头,接着一个或多个空格,然后是列表项目的文字,直到行末。转换时,在最开始插入一行 <ul>,最后插入一行 </ul>;对于每行,* Item 转换为 <li>Item</li>。本题中的无序列表只有一层,不会出现缩进的情况。
      ●行内:对于区块中的内容,有以下两种行内结构。
      ○强调:_Text_ 转换为 <em>Text</em>。强调不会出现嵌套,每行中 _ 的个数一定是偶数,且不会连续相邻。注意 _Text_ 的前后不一定是空格字符。
      ○超级链接:[Text](Link) 转换为 <a href="Link">Text</a>。超级链接和强调可以相互嵌套,但每种格式不会超过一层。
    输入格式
      输入由若干行组成,表示一个用本题规定的 Markdown 语法撰写的文档。
    输出格式
      输出由若干行组成,表示输入的 Markdown 文档转换成产生的 HTML 代码。
    样例输入
    # Hello
    Hello, world!
    样例输出
    <h1>Hello
    <p>Hello, world!
    评测用例规模与约定
      本题的测试点满足以下条件:
      ●本题每个测试点的输入数据所包含的行数都不超过100,每行字符的个数(包括行末换行符)都不超过100。
      ●除了换行符之外,所有字符都是 ASCII 码 32 至 126 的可打印字符。
      ●每行行首和行末都不会出现空格字符。
      ●输入数据除了 Markdown 语法所需,内容中不会出现 #*_[]()<>& 这些字符。
      ●所有测试点均符合题目所规定的 Markdown 语法,你的程序不需要考虑语法错误的情况。

代码:

#include <iostream>
#include <string>

using namespace std;
const int MAXN = 110;
string arr[MAXN];

int main() {
    string s;
    int n = 0;
    bool op = true;

    while (getline(cin, s)) {
        arr[n++] = s;
    }

    for (int i = 0; i < n; i++) {
        string s;
        int pos = 0;
        while (pos < arr[i].length()) {
            if (arr[i][pos] == '_') {
                s += "<em>";
                ++pos;
                while (pos < arr[i].length() && arr[i][pos] != '_')
                    s += arr[i][pos++];
                ++pos;
                s += "</em>";
            } else {
                s += arr[i][pos++];
            }
        }

        arr[i] = s;
        s.clear();
        pos = 0;
        while (pos < arr[i].length()) {
            if (arr[i][pos] == '[') {
                string a, b;
                ++pos;
                while (pos < arr[i].length() && arr[i][pos] != ']')
                    a += arr[i][pos++];
                pos += 2;
                while (pos < arr[i].length() && arr[i][pos] != ')')
                    b += arr[i][pos++];
                ++pos;
                s += "<a href=\"" + b + "\">" + a + "</a>";
            } else {
                s += arr[i][pos++];
            }
        }
        arr[i] = s;
    }

    for (int i = 0; i < n; i++) {
        s = arr[i];
        if (s[0] == '#') {
            int cnt = 0;
            while (cnt < s.length() && s[cnt] == '#')
                ++cnt;
            cout << "<h" << cnt << ">";
            int pos = cnt;
            while (pos < s.length() && s[pos] == ' ')
                ++pos;
            cout << s.substr(pos);
            cout << "</h" << cnt << ">" << endl;
        } else if (s[0] == '*') {
            cout << "<ul>" << endl;
            while (arr[i][0] == '*') {
                int pos = 1;
                while (pos < arr[i].length() && arr[i][pos] == ' ')
                    ++pos;
                cout << "<li>" << arr[i].substr(pos) << "</li>" << endl;
                ++i;
            }
            i--;
            cout << "</ul>" << endl;
        } else if (s.empty()) {
            continue;
        } else {
            if (op) {
                cout << "<p>";
                op = false;
            }
            //111
            cout << s;
            if (!op && (i + 1 == n) || (arr[i + 1].empty())) {
                op = true;
                cout << "</p>";
            }
            cout << endl;
        }
    }
    return 0;
}

  1. 地铁修建
    问题描述
      A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁。
      地铁由很多段隧道组成,每段隧道连接两个交通枢纽。经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽。
      现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致。而每家公司最多只能修建一条候选隧道。所有公司同时开始施工。
      作为项目负责人,你获得了候选隧道的信息,现在你可以按自己的想法选择一部分隧道进行施工,请问修建整条地铁最少需要多少天。
    输入格式
      输入的第一行包含两个整数n, m,用一个空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
      第2行到第m+1行,每行包含三个整数a, b, c,表示枢纽a和枢纽b之间可以修建一条隧道,需要的时间为c天。
    输出格式
      输出一个整数,修建整条地铁线路最少需要的天数。
    样例输入
    6 6
    1 2 4
    2 3 4
    3 6 7
    1 4 2
    4 5 5
    5 6 6
    样例输出
    6
    样例说明
      可以修建的线路有两种。
      第一种经过的枢纽依次为1, 2, 3, 6,所需要的时间分别是4, 4, 7,则整条地铁线需要7天修完;
      第二种经过的枢纽依次为1, 4, 5, 6,所需要的时间分别是2, 5, 6,则整条地铁线需要6天修完。
      第二种方案所用的天数更少。
    评测用例规模与约定
      对于20%的评测用例,1 ≤ n ≤ 10,1 ≤ m ≤ 20;
      对于40%的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000;
      对于60%的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000,1 ≤ c ≤ 1000;
      对于80%的评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000;
      对于100%的评测用例,1 ≤ n ≤ 100000,1 ≤ m ≤ 200000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000。
      所有评测用例保证在所有候选隧道都修通时1号枢纽可以通过隧道到达其他所有枢纽。

代码:

#include <iostream>
#include <cstring>
#define ac cin.tie(0); cin.sync_with_stdio(0);

using namespace std;

const int N = 100010, M = 400010;
int head[N], ver[M], edge[M], nxt[M], dist[N], q[N], cnt = 0;
int n, m;

void add(int a, int b, int c){
    ver[++ cnt] = b;
    edge[cnt] = c;
    nxt[cnt] = head[a];
    head[a] = cnt;
}

bool check(int mid){
    memset(dist, 0x3f, sizeof(dist));

    dist[1] = 0;
    q[0] = 1;
    int hh = 0, tt = 1;
    while(hh != tt){
        int cur = q[hh ++];
        if(hh == N)
            hh = 0;
        for(int i = head[cur]; i; i = nxt[i]){
            int u = ver[i];
            if(edge[i] > mid)
                continue;
            if(dist[u] > dist[cur] + 1){
                dist[u] = dist[cur] + 1;
                q[tt ++] = u;
                if(tt == N)
                    tt = 0;
            }
        }
    }
    return dist[n] != 0x3f3f3f3f;
}

int main(){
    ac
    int a, b, c;

    cin >> n >> m;
    while(m --){
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }

    int le = 0, ri = 1e6, mid;
    while(le < ri){
        mid = le + ri >> 1;
        if(check(mid)) ri = mid;
        else    le = mid + 1;
    }
    cout << ri;
    return 0;
}

  1. 引水入城
    问题描述
      MF城建立在一片高原上。由于城市唯一的水源是位于河谷地带的湖中,人们在坡地上修筑了一片网格状的抽水水管,以将湖水抽入城市。如下图所示:
    在这里插入图片描述
      这片管网由 n 行 m 列节点(红色,图中 n = 5,m = 6),横向管道(紫色)和纵向管道(橙色)构成。
      行和列分别用 1 到 n 的整数和 1 到 m 的整数表示。第 1 行的任何一个节点均可以抽取湖水,湖水到达第 n 行的任何一个节点即算作引入了城市。
      除第一行和最后一行外,横向相邻或纵向相邻的两个节点之间一定有一段管道,每一段管道都有各自的最大的抽水速率,并需要根据情况选择抽水还是放水。对于纵向的管道(橙色),允许从上方向下方抽水或从下方向上方放水;如果从图中的上方向下方抽水,那么单位时间内能通过的水量不能超过管道的最大速率;如果从下方向上方放水,因为下方海拔较高,因此可以允许有任意大的水量。对于横向的管道(紫色),允许从左向右或从右向左抽水,不允许放水,两种情况下单位时间流过的水量都不能超过管道的最大速率。
      现在MF城市的水务负责人想知道,在已知每个管道单位时间容量的情况下,MF城每单位时间最多可以引入多少的湖水。
    输入格式
      由于输入规模较大,我们采用伪随机生成的方式生成数据。
      每组数据仅一行包含 6 个非负整数 n, m, A, B, Q, X0。其中,n 和 m 如前文所述,表示管网的大小,保证 2 ≤ n, m ≤ 5000;保证 1 ≤ A, B, Q, X0 ≤ 109。
      A, B, Q, X0 是数据生成的参数,我们用如下的方式定义一个数列 { Xi }:
      Xi+1 = ( AXi + B) mod Q, (i ≥ 0)
      我们将数列的第 1 项到第 (n-1)m 项作为纵向管道的单位时间容量,其中 X(s-1)m+t 表示第 s 行第 t 列的节点到第 s+1 行第 t 列管道单位时间的容量;将数列的第 (n-1)m+1 项到第 (n-1)m+(n-2)(m-1) 项(即接下来的 (n-2)(m-1) 项)作为横向管道的单位时间容量,其中 X(n-1)m+(s-2)(m-1)+t 表示第 s 行第 t 列的节点到第 s 行第 t+1 列管道单位时间的容量。
    输出格式
      输出一行一个整数,表示MF城每单位时间可以引入的水量。
      注意计算过程中有些参数可能超过32位整型表示的最大值,请注意使用64位整型存储相应数据。
    样例输入
    3 3 10 3 19 7
    样例输出
    38
    样例说明
      使用参数得到数列 { Xi }={ 7, 16, 11, 18, 12, 9, 17, 2, 4, … },按照输入格式可以得到每个管道的最大抽水量如下图所示:
    在这里插入图片描述
      在标准答案中,单位时间可以引水 38 单位。所有纵向管道均向下抽水即可,不需要横向管道抽水,也不需要向上放水。
    样例输入
    2 5 595829232 749238243 603779819 532737791
    样例输出
    1029036148
    样例输入
    5 2 634932890 335818535 550589587 977780683
    样例输出
    192923706
    样例输入
    5 5 695192542 779962396 647834146 157661239
    样例输出
    1449991168
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值