ccf-csp 2018冬季真题题解


  1. 小明上学
    题目背景
      小明是汉东省政法大学附属中学的一名学生,他每天都要骑自行车往返于家和学校。为了能尽可能充足地睡眠,他希望能够预计自己上学所需要的时间。他上学需要经过数段道路,相邻两段道路之间设有至多一盏红绿灯。
      京州市的红绿灯是这样工作的:每盏红绿灯有红、黄、绿三盏灯和一个能够显示倒计时的显示牌。假设红绿灯被设定为红灯 r 秒,黄灯 y 秒,绿灯 g 秒,那么从 0 时刻起,[0,r) 秒内亮红灯,车辆不许通过;[r, r+g) 秒内亮绿灯,车辆允许通过;[r+g, r+g+y) 秒内亮黄灯,车辆不许通过,然后依次循环。倒计时的显示牌上显示的数字 l(l > 0)是指距离下一次信号灯变化的秒数。
    问题描述
      一次上学的路上,小明记录下了经过每段路的时间,和各个红绿灯在小明到达路口时的颜色和倒计时秒数。希望你帮忙计算此次小明上学所用的时间。
    输入格式
      输入的第一行包含空格分隔的三个正整数 r、y、g,表示红绿灯的设置。这三个数均不超过 106。
      输入的第二行包含一个正整数 n(n ≤ 100),表示小明总共经过的道路段数和看到的红绿灯数目。
      接下来的 n 行,每行包含空格分隔的两个整数 k、t。k=0 表示经过了一段道路,耗时 t 秒,此处 t 不超过 106;k=1、2、3 时,分别表示看到了一个红灯、黄灯、绿灯,且倒计时显示牌上显示的数字是 t,此处 t 分别不会超过 r、y、g。
    输出格式
      输出一个数字,表示此次小明上学所用的时间。
    样例输入
    30 3 30
    8
    0 10
    1 5
    0 11
    2 2
    0 6
    0 3
    3 10
    0 3
    样例输出
    70
    样例说明
      小明先经过第一段道路,用时 10 秒,然后等待 5 秒的红灯,再经过第二段道路,用时 11 秒,然后等待 2 秒的黄灯和 30 秒的红灯,再经过第三段、第四段道路,分别用时6、3秒,然后通过绿灯,再经过最后一段道路,用时 3 秒。共计 10 + 5 + 11 + 2 + 30 + 6 + 3 + 3=70 秒。
    评测用例规模与约定
      测试点 1, 2 中不存在任何信号灯。
      测试点 3, 4 中所有的信号灯在被观察时均为绿灯。
      测试点 5, 6 中所有的信号灯在被观察时均为红灯。
      测试点 7, 8 中所有的信号灯在被观察时均为黄灯。
      测试点 9, 10 中将出现各种可能的情况。

代码:

#include <iostream>

using namespace std;

int main(){
    int n, ans = 0, r, y, g, k, t;

    cin >> r >> y >> g;
    cin >> n;
    while(n --){
        cin >> k >> t;
        if(k == 0)  ans += t;
        else if(k == 1) ans += t;
        //黄灯的话,还需要等待一个红灯周期
        else if(k == 2) ans += t + r;
    }
    cout << ans << endl;
    return 0;
}

  1. 小明放学
    题目背景
      汉东省政法大学附属中学所在的光明区最近实施了名为“智慧光明”的智慧城市项目。具体到交通领域,通过“智慧光明”终端,可以看到光明区所有红绿灯此时此刻的状态。小明的学校也安装了“智慧光明”终端,小明想利用这个终端给出的信息,估算自己放学回到家的时间。
    问题描述
      一次放学的时候,小明已经规划好了自己回家的路线,并且能够预测经过各个路段的时间。同时,小明通过学校里安装的“智慧光明”终端,看到了出发时刻路上经过的所有红绿灯的指示状态。请帮忙计算小明此次回家所需要的时间。
    输入格式
      输入的第一行包含空格分隔的三个正整数 r、y、g,表示红绿灯的设置。这三个数均不超过 106。
      输入的第二行包含一个正整数 n,表示小明总共经过的道路段数和路过的红绿灯数目。
      接下来的 n 行,每行包含空格分隔的两个整数 k、t。k=0 表示经过了一段道路,将会耗时 t 秒,此处 t 不超过 106;k=1、2、3 时,分别表示出发时刻,此处的红绿灯状态是红灯、黄灯、绿灯,且倒计时显示牌上显示的数字是 t,此处 t 分别不会超过 r、y、g。
    输出格式
      输出一个数字,表示此次小明放学回家所用的时间。
    样例输入
    30 3 30
    8
    0 10
    1 5
    0 11
    2 2
    0 6
    0 3
    3 10
    0 3
    样例输出
    46
    样例说明
      小明先经过第一段路,用时 10 秒。第一盏红绿灯出发时是红灯,还剩 5 秒;小明到达路口时,这个红绿灯已经变为绿灯,不用等待直接通过。接下来经过第二段路,用时 11 秒。第二盏红绿灯出发时是黄灯,还剩两秒;小明到达路口时,这个红绿灯已经变为红灯,还剩 11 秒。接下来经过第三、第四段路,用时 9 秒。第三盏红绿灯出发时是绿灯,还剩 10 秒;小明到达路口时,这个红绿灯已经变为红灯,还剩两秒。接下来经过最后一段路,用时 3 秒。共计 10+11+11+9+2+3 = 46 秒。
    评测用例规模与约定
      有些测试点具有特殊的性质:
      * 前 2 个测试点中不存在任何信号灯。
      测试点的输入数据规模:
      * 前 6 个测试点保证 n ≤ 103。
      * 所有测试点保证 n ≤ 105。

代码:

#include <iostream>

using namespace std;

int main(){
    long long r, y, g, n, sum;
    long long ans = 0;

    cin >> r >> y >> g >> n;
    sum = r + y + g;
    while(n --){
        long long k, t;
        cin >> k >> t;
        if(k == 0)  ans += t;
        else{
        	//转变坐标
            if(k == 1)  t = r - t;
            else if(k == 2) t = r + g + y - t;
            else if(k == 3) t = r + g - t;

            t = (t + ans) % sum;
            //红灯周期
            if(t < r)
                ans += r - t;
            //黄灯周期
            else if(t >= r + g)
                ans += sum - t + r;
        }
    }
    cout << ans << endl;
    return 0;
}

  1. CIDR合并
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    样例输入
    2
    1
    2
    样例输出
    1.0.0.0/8
    2.0.0.0/8
    样例输入
    2
    10/9
    10.128/9
    样例输出
    10.0.0.0/8
    样例输入
    2
    0/1
    128/1
    样例输出
    0.0.0.0/0
    在这里插入图片描述
    在这里插入图片描述

代码:

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

using namespace std;

const int N = 100010;

int n;
struct IP
{
    string v;
    int k;
    bool operator< (const IP& t) const
    {
        if (v != t.v) return v < t.v;
        return k < t.k;
    }
    bool is_substr(IP& t)
    {
        if (t.k < k) return false;
        if (v.substr(0, k) != t.v.substr(0, k)) return false;
        return true;
    }

    int get_number(string str)
    {
        int res = 0;
        for (int i = 0; i < 8; i ++ )
            res = res * 2 + str[i] - '0';
        return res;
    }

    void print()
    {
        for (int i = 0; i < 32; i += 8)
        {
            if (i) printf(".");
            printf("%d", get_number(v.substr(i, 8)));
        }
        printf("/%d\n", k);
    }
}ip[N];

IP merge(IP& a, IP& b)
{
    IP res;
    res.k = -1;
    if (a.k != b.k) return res;
    if (a.v.substr(0, a.k - 1) != b.v.substr(0, b.k - 1)) return res;
    res.k = a.k - 1;
    res.v = a.v.substr(0, a.k - 1);
    while (res.v.size() <= 32) res.v += '0';
    return res;
}

int main()
{
    scanf("%d", &n);
    char str[20];
    int d[4];
    for (int i = 0; i < n; i ++ )
    {
        scanf("%s", str);
        memset(d, 0, sizeof d);
        int cnt = 0;
        ip[i].k = -1;
        for (int j = 0; str[j]; j ++ )
        {
            if (str[j] == '/')
            {
                ip[i].k = atoi(str + j + 1);
                break;
            }
            if (str[j] == '.') continue;
            while (str[j] && str[j] != '.' && str[j] != '/')
                d[cnt] = d[cnt] * 10 + str[j ++ ] - '0';
            j -- ;
            cnt ++ ;
        }
        for (int j = 0; j < 4; j ++ )
            for (int k = 7; k >= 0; k -- )
                if (d[j] >> k & 1)
                    ip[i].v += '1';
                else
                    ip[i].v += '0';
        if (ip[i].k == -1) ip[i].k = cnt * 8;
    }

    sort(ip, ip + n);

    int k = 1;
    for (int i = 1; i < n; i ++ )
        if (!ip[k - 1].is_substr(ip[i]))
            ip[k ++ ] = ip[i];
    n = k;

    k = 1;
    for (int i = 1; i < n; i ++ )
    {
        ip[k ++ ] = ip[i];
        while (k >= 2)
        {
            auto t = merge(ip[k - 2], ip[k - 1]);
            if (t.k != -1)
            {
                k -= 2;
                ip[k ++ ] = t;
            }
            else break;
        }
    }

    n = k;
    for (int i = 0; i < n; i ++ )
        ip[i].print();
    return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/900874/

  1. 数据中心
    在这里插入图片描述
    在这里插入图片描述
    样例输入
    4
    5
    1
    1 2 3
    1 3 4
    1 4 5
    2 3 8
    3 4 2
    样例输出
    4
    样例说明
      下图是样例说明。
    在这里插入图片描述
    在这里插入图片描述

代码:

//二分 + 并查集
#include <iostream>

using namespace std;
const int MAXN = 5e5 + 10;
int pre[MAXN], n, m, r;
struct node {
    int a, b, c;
};
node arr[MAXN];

int find(int a) {
    return a == pre[a] ? a : pre[a] = find(pre[a]);
}

bool check(int mid) {
    int a, b, cnt = 1;
    for (int i = 1; i <= n; i++)
        pre[i] = i;
    for (int i = 0; i < m; i++) {
        if (arr[i].c > mid)
            continue;
        a = find(arr[i].a);
        b = find(arr[i].b);
        if (a != b) {
            pre[a] = b;
            ++cnt;
        }
    }
    return cnt == n;
}

int main() {
    cin >> n >> m >> r;
    for (int i = 0; i < m; i++)
        cin >> arr[i].a >> arr[i].b >> arr[i].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 << endl;
    return 0;
}

  1. 管道清洁
    在这里插入图片描述样例输入
    3 0 1
    4 4
    1 2 A
    2 3 B
    3 4 C
    4 1 D
    5 7
    1 2 B
    2 3 B
    3 1 D
    2 4 A
    4 3 C
    1 5 C
    5 2 D
    5 7
    1 2 B
    2 3 B
    3 1 C
    2 4 A
    4 3 D
    1 5 C
    5 2 D
    样例输出
    4
    -1
    8
    样例说明
      这3组数据如下图所示
      在这里插入图片描述
    在这里插入图片描述

代码:

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

using namespace std;

const int N = 210, M = (500 + N) * 2 + 10, INF = 1e6;

int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], dist[N], pre[N], incf[N];
bool st[N];
int din[N], dout[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}

bool spfa()
{
    int hh = 0, tt = 1;
    memset(dist, 0x3f, sizeof dist);
    memset(incf, 0, sizeof incf);
    q[0] = S, dist[S] = 0, incf[S] = INF;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (f[i] && dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                pre[j] = i;
                incf[j] = min(f[i], incf[t]);
                if (!st[j])
                {
                    q[tt ++ ] = j;
                    if (tt == N) tt = 0;
                    st[j] = true;
                }
            }
        }
    }
    return incf[T] > 0;
}

int EK(int tot)
{
    int flow = 0, cost = 0;
    while (spfa())
    {
        int t = incf[T];
        flow += t, cost += t * dist[T];
        for (int i = T; i != S; i = e[pre[i] ^ 1])
        {
            f[pre[i]] -= t;
            f[pre[i] ^ 1] += t;
        }
    }
    if (flow != tot) return -1;
    return cost;
}

int main()
{
    int C, E;
    scanf("%d%*d%d", &C, &E);
    while (C -- )
    {
        memset(h, -1, sizeof h);
        idx = 0;
        memset(din, 0, sizeof din);
        memset(dout, 0, sizeof dout);
        scanf("%d%d", &n, &m);
        S = 0, T = n + 1;
        int down_cost = 0;
        while (m -- )
        {
            int a, b;
            char c;
            scanf("%d %d %c", &a, &b, &c);
            int down, up;
            if (c == 'A') down = 1, up = INF, down_cost += E;
            else if (c == 'B') down = up = 1, down_cost += E;
            else if (c == 'C') down = 0, up = INF;
            else down = 0, up = 1;
            add(a, b, up - down, E);
            din[b] += down, dout[a] += down;
        }
        int tot = 0;
        for (int i = 1; i <= n; i ++ )
            if (din[i] > dout[i])
            {
                add(S, i, din[i] - dout[i], 0);
                tot += din[i] - dout[i];
            }
            else add(i, T, dout[i] - din[i], 0);

        int c = EK(tot);
        if (c != -1) c += down_cost;
        printf("%d\n", c);
    }
    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/958977/

更多历年题解戳这里:ccf-csp 历年真题题解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值