第十三届蓝桥杯大赛软件赛决赛(C/C++ 大学B组)


   更新中…


试题 A: 2022

本题总分: 5 5 5


【问题描述】

  将 2022 2022 2022 拆分成 10 10 10 个互不相同的正整数之和,总共有多少种拆分方法?

  注意交换顺序视为同一种方法,例如 2022 = 1000 + 1022 2022 = 1000 + 1022 2022=1000+1022 2022 = 1022 + 1000 2022 = 1022 + 1000 2022=1022+1000 就视为同一种方法。

【答案提交】

  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个由大写字母组成的字符串,在提交答案时只填写这个字符串,填写多余的内容将无法得分。


379187662194355221


k 部分互异分拆数


  定义 p d k n pd_kn pdkn n n n 分拆成 k k k 个不同的部分的方案数,即下列方程 : : n = r 1 + r 2 + ⋯ + r k , r 1 > r 2 > ⋯ > r k ≥ 1 n = r_1 +r_2 + \cdots +r_k,\quad r_1 > r_2 >\cdots>r_k \geq 1 n=r1+r2++rk,r1>r2>>rk1  的解的数量,两边同时减去 k k k : : n − k = d 1 + d 2 + ⋯ + d k , d 1 > d 2 > ⋯ > d k ≥ 0 , n - k = d_1 +d_2 + \cdots +d_k,\quad d_1 > d_2 >\cdots>d_k \geq 0, nk=d1+d2++dk,d1>d2>>dk0  由于 d i d_i di 互异,故仅能有一个部分为 0 0 0,以此为界,划分成 : : n − k = d 1 + d 2 + ⋯ + d k − 1 + 0 , d 1 > d 2 > ⋯ > d k ≥ 0 , n − k = g 1 + g 2 + ⋯ + g k , g 1 > g 2 > ⋯ > g k ≥ 1 , n - k = d_1 +d_2 + \cdots +d_{k-1} + 0,\quad d_1 > d_2 >\cdots>d_k \geq 0,\\n - k = g_1 +g_2 + \cdots +g_k,\quad g_1 > g_2 >\cdots>g_k \geq 1, nk=d1+d2++dk1+0,d1>d2>>dk0nk=g1+g2++gk,g1>g2>>gk1  两部分,显然有递推式 : : p d k n = p d k − 1 ( n − k ) + p d k ( n − k ) . pd_kn=pd_{k-1}(n-k) + pd_k(n-k). pdkn=pdk1(nk)+pdk(nk).

#include <stdio.h>

const int N = 2022, k = 10;

long long pd[N + 1][k + 1]{1};

int main() {
    for (int i = 1; i <= N; ++i)
        for (int j = 1; j <= k; ++j)
            if (i >= j) pd[i][j] = pd[i - j][j] + pd[i - j][j - 1];
    printf("%lld", pd[N][k]);
}

  上来就这么劲爆的吗,难度


试题 B: 钟表

本题总分: 5 5 5


【问题描述】

  在 12 12 12 小时制的钟表中,有分针、时针、秒针来表示时间。记分针和时针之间的夹角度数为 A ( 0 ≤ A ≤ 180 ) A(0 ≤ A ≤ 180) A0A180、分针和秒针之间的夹角度数为 B ( 0 ≤ B ≤ 180 ) B(0 ≤ B ≤ 180) B0B180。而恰好在 s s s f f f m m m 秒时,满足条件 A = 2 B A = 2B A=2B 0 ≤ s ≤ 6 ; 0 ≤ f < 60 ; 0 ≤ m < 60 0 ≤ s ≤ 6;0≤ f < 60;0≤m < 60 0s60f<600m<60,请问 s , f , m s, f,m s,f,m 分别是多少。

  注意时针、分针、秒针都围绕中心匀速转动。

  提交格式为三个由一个空格隔开的整数,分别表示 s , f , m s, f,m s,f,m。如 3   11   58 3\ 11\ 58 3 11 58 表示 3 3 3 11 11 11 58 58 58 秒。

【答案提交】

  这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为三个由一个空格隔开的整数,在提交答案时只填写为三个由一个空格隔开的整数,填写多余的内容将无法得分。


4 48 0


  为了一定程度上的运算简便,这里周角为 60 60 60 度,

  然后 B F \small \rm BF BF 就完了。

#include <stdio.h>

double fabs(double x) { return x > 0 ? x : -x; }

double abs(double angle) {
    angle = fabs(angle);
    if (angle > 30) return 60 - angle;
    return angle;
}

int main() {
    for (int s = 0; s <= 6; ++s)
        for (int f = 0; f < 60; ++f)
            for (int m = 0; m < 60; ++m) {
                double second = m;
                double minute = f + second / 60;
                double hour = (s * 60 + minute) / 12;
                double B = abs(minute - second);
                double A = abs(minute - hour);
                if (fabs(A - 2 * B) < 1e-10)
                    printf("%d %d %d\n", s, f, m);
            }
}

  还好我的好 b r o \small\rm bro bro 告诉我 0   0   0 0\ 0\ 0 0 0 0 是作废答案,

  不然我就直接填了。

  其实也可以类似程序中计算角度的过程来列出三元方程组,算的还快一点。


试题 C: 卡牌

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 10 10 10


【问题描述】

  这天,小明在整理他的卡牌。

  他一共有 n n n 种卡牌,第 i i i 种卡牌上印有正整数数 i ( i ∈ [ 1 , n ] ) i(i \in [1, n]) i(i[1,n]),且第 i i i 种卡牌现有 a i a_i ai 张。

  而如果有 n n n 张卡牌,其中每种卡牌各一张,那么这 n n n 张卡牌可以被称为一套牌。小明为了凑出尽可能多套牌,拿出了 m m m 张空白牌,他可以在上面写上数i,将其当做第 i i i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i i i 种牌最多手写 b i b_i bi 张。

  请问小明最多能凑出多少套牌?

【输入格式】

  输入共 3 3 3 行,第一行为两个正整数 n , m n, m n,m

  第二行为 n n n 个正整数 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an

  第三行为 n n n 个正整数 b 1 , b 2 , ⋯   , b n b_1, b_2, \cdots, b_n b1,b2,,bn

【输出格式】

  一行,一个整数表示答案。

【样例输入】

4 5
1 2 3 4
5 5 5 5

【样例输出】

3

【样例说明】

  这 5 5 5 张空白牌中,拿 2 2 2 张写 1 1 1,拿 1 1 1 张写 2 2 2,这样每种牌的牌数就变为了 3 , 3 , 3 , 4 3, 3, 3, 4 3,3,3,4,可以凑出 3 3 3 套牌,剩下 2 2 2 张空白牌不能再帮助小明凑出一套。

【评测用例规模与约定】

  对于 30 % 30\% 30% 的数据,保证 n ≤ 2000 n ≤ 2000 n2000
  对于 100 % 100\% 100% 的数据,保证 n ≤ 2 × 1 0 5 ; a i , b i ≤ n ; m ≤ n 2 n ≤ 2 × 10^5; a_i, b_i ≤ n; m ≤ n^2 n2×105;ai,bin;mn2


#include <stdio.h>

const int N = 2 * 1e5;

int n, A[N], B[N];

long long m;

int main() {
    scanf("%d %lld", &n, &m);
    for (int i = 0; i < n; ++i) scanf("%d", A + i);
    for (int i = 0; i < n; ++i) scanf("%d", B + i);
    int l = 0, r = 2 * n;
    while (l < r) {
        int flag = 1, mid = l + r + 1 >> 1;
        long long buf = m;
        for (int i = 0; i < n; ++i) {
            if (A[i] >= mid) continue;
            if (A[i] + B[i] < mid || mid - A[i] > buf) {
                flag = 0;
                break;
            }
            buf -= mid - A[i];
        }
        if (flag) l = mid; else r = mid - 1;
    }
    printf("%d", l);
}

  二分判定答案的模板。


试题 D: 最大数字

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 10 10 10


【问题描述】

  给定一个正整数 N N N。你可以对 N N N 的任意一位数字执行任意次以下 2 2 2 种操作 : :

   1. 1. 1. 将该位数字加 1 1 1。如果该位数字已经是 9 9 9,加 1 1 1 之后变成 0 0 0
   2. 2. 2. 将该位数字减 1 1 1。如果该位数字已经是 0 0 0,减 1 1 1 之后变成 9 9 9

  你现在总共可以执行 1 1 1 号操作不超过 A A A 次, 2 2 2 号操作不超过 B B B 次。

  请问你最大可以将 N N N 变成多少?

【输入格式】

  第一行包含 3 3 3 个整数 : : N , A , B N, A, B N,A,B

【输出格式】

  一个整数代表答案。

【样例输入】

123 1 2

【样例输出】

933

【样例说明】

  对百位数字执行 2 2 2 2 2 2 号操作,对十位数字执行 1 1 1 1 1 1 号操作。

【评测用例规模与约定】

  对于 30 % 30\% 30% 的数据, 1 ≤ N ≤ 100 ; 0 ≤ A , B ≤ 10 1 ≤ N ≤ 100; 0 ≤ A, B ≤ 10 1N100;0A,B10
  对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 17 ; 0 ≤ A , B ≤ 100 1 ≤ N ≤ 10^{17}; 0 ≤ A, B ≤ 100 1N1017;0A,B100


#include <stdio.h>

char N[20];

long long ans = 0;

int A, B;

inline int min(int a, int b) { return a < b ? a : b; }

void dfs(int i, long long val) {
    if (N[i]) {
        int a = min(A, '9' - N[i]);
        A -= a;
        dfs(i + 1, val * 10 + N[i] + a - '0');
        A += a;
        if (N[i] - '0' < B) {
            int b = N[i] - '0' + 1;
            B -= b;
            dfs(i + 1, val * 10 + 9);
            B += b;
        }
    } else if (val > ans) ans = val;
}

int main() { scanf("%s %d %d", N, &A, &B), dfs(0, 0), printf("%lld", ans); }

  看一眼数据范围就知道,贪心暴搜。


试题 E: 出差

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15


【问题描述】

   A \rm A A 国有 N N N 个城市,编号为 1 ⋯ N \rm 1 \cdots N 1N。小明是编号为 1 1 1 的城市中一家公司的员工,今天突然接到了上级通知需要去编号为 N \rm N N 的城市出差。

  由于疫情原因,很多直达的交通方式暂时关闭,小明无法乘坐飞机直接从城市 1 1 1 到达城市 N \rm N N,需要通过其他城市进行陆路交通中转。小明通过交通信息网,查询到了 M M M 条城市之间仍然还开通的路线信息以及每一条路线需要花费的时间。

  同样由于疫情原因,小明到达一个城市后需要隔离观察一段时间才能离开该城市前往其他城市。通过网络,小明也查询到了各个城市的隔离信息。(由于小明之前在城市 1 1 1,因此可以直接离开城市 1 1 1,不需要隔离)

  由于上级要求,小明希望能够尽快赶到城市 N \rm N N,因此他求助于你,希望你能帮他规划一条路线,能够在最短时间内到达城市 N \rm N N

【输入格式】

  第 1 1 1 : : 两个正整数 N , M N, M N,M N N N 表示 A \rm A A 国的城市数量, M M M 表示未关闭的路线数量

  第 2 2 2 : : N N N 个正整数,第 i i i 个整数 C i C_i Ci 表示到达编号为 i i i 的城市后需要隔离的时间

  第 3 ⋯ M + 2 3 \cdots M + 2 3M+2 : : 每行 3 3 3 个正整数, u , v , c u, v, c u,v,c,表示有一条城市 u u u 到城市 v v v 的双向路线仍然开通着,通过该路线的时间为 c c c

【输出格式】

  第 1 1 1 : : 1 1 1 个正整数,表示小明从城市 1 1 1 出发到达城市 N \rm N N 的最短时间(到达城市 N \rm N N,不需要计算城市 N \rm N N 的隔离时间)

【样例输入】

4 4
5 7 3 4
1 2 4
1 3 5
2 4 3
3 4 5

【样例输出】

13

【样例说明】

   [ 1 ( 5 ) ] − − 4 − − [ 2 ( 7 ) ] [1(5)] --4-- [2(7)] [1(5)]4[2(7)]
    ∣ ∣ |\qquad\qquad\qquad\quad|
    ∣ ∣ |\qquad\qquad\qquad\quad|
    5      3 5\qquad\qquad\qquad\ \:\:3 5 3
    ∣ ∣ |\qquad\qquad\qquad\quad|
    ∣ ∣ |\qquad\qquad\qquad\quad|
   [ 3 ( 3 ) ] − − 5 − − [ 4 ( 4 ) ] [3(3)] --5-- [4(4)] [3(3)]5[4(4)]

  路线 1 1 1 : : 1 − > 2 − > 4 1 -> 2 -> 4 1>2>4,时间为 4 + 7 4+7 4+7(隔离) + 3 = 14 +3=14 +3=14
  路线 2 2 2 : : 1 − > 3 − > 4 1 -> 3 -> 4 1>3>4,时间为 5 + 3 5+3 5+3(隔离) + 5 = 13 +5=13 +5=13

【评测用例规模与约定】

  对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1000 , 1 ≤ M ≤ 10000 , 1 ≤ C i ≤ 200 , 1 ≤ u , v ≤ N , 1 ≤ c ≤ 1000 1 ≤ N ≤ 1000 , 1 ≤ M ≤ 10000, 1 ≤ C_i ≤ 200, 1 ≤ u, v ≤ N, 1 ≤ c ≤ 1000 1N1000,1M10000,1Ci200,1u,vN,1c1000


#include <stdio.h>
#include <utility>
#include <queue>

const int N = 1e3 + 1, M = 4 * 1e4 + 9;

int linked[M], next[M], ver[M], val[M], cur = 0;

inline void link(int u, int v, int k) { ++cur, next[cur] = linked[u], linked[u] = cur, ver[cur] = v, val[cur] = k; }

std::priority_queue<std::pair<int, int>> q;

int n, m, u, v, c, C[N], dist[N];

bool visited[N];

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", C + i), dist[i] = 0x3f3f3f3f;
    C[1] = 0;
    while (m--) {
        scanf("%d %d %d", &u, &v, &c);
        link(u, v, C[u] + c);
        link(v, u, C[v] + c);
    }
    dist[1] = 0;
    q.push({0, 1});
    while (q.size()) {
        u = q.top().second, q.pop();
        if (visited[u]) continue;
        visited[u] = 1;
        for (cur = linked[u]; cur; cur = next[cur])
            if (dist[ver[cur]] > dist[u] + val[cur]) {
                dist[ver[cur]] = dist[u] + val[cur];
                q.push({-dist[ver[cur]], ver[cur]});
            }
    }
    printf("%d", dist[n]);
}

  模板 × 3 \times 3 ×3


试题 F: 费用报销

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15


【问题描述】

  小明在出差结束后返回了公司所在的城市,在填写差旅报销申请时,粗心的小明发现自己弄丢了出差过程中的票据。

  为了弥补小明的损失,公司同意小明用别的票据进行报销,但是公司财务要求小明提交的票据中任意两张的日期差不小于 K K K 天,且总金额不得超过实际差旅费用 M M M

  比如财务要求 K = 7 K = 7 K=7 时,若小明提交了一张 1 1 1 8 8 8 日的票据,小明就不能提交 1 1 1 2 2 2 日至 1 1 1 14 14 14 日之间的其他票据, 1 1 1 1 1 1 日及之前和 1 1 1 15 15 15 日及之后的票据则可以提交。

  公司的同事们一起给小明凑了 N N N 张票据,小明现在想要请你帮他整理一下,从中选取出符合财务要求的票据,并使总金额尽可能接近 M M M

  需要注意,由于这些票据都是同一年的,因此 12 12 12 月底的票据不会影响到 1 1 1 月初票据的提交。这一年不是闰年。

【输入格式】

  第 1 1 1 : : 3 3 3 个整数, N , M , K N, M, K N,M,K

  第 2 ⋯ N + 1 2 \cdots N + 1 2N+1 : : 每行 3 3 3 个整数 m i , d i , v i m_i, d_i, v_i mi,di,vi,第 i + 1 i + 1 i+1 行表示第 i i i 张票据时间的月份 m i m_i mi 和日期 d i d_i di v i v_i vi 表示该票据的面值

【输出格式】

  第 1 1 1 : : 1 1 1 个整数,表示小明能够凑出的最大报销金额

【样例输入】

4 16 3
1 1 1
1 3 2
1 4 4
1 6 8

【样例输出】

10

【样例说明】

  选择 1 1 1 3 3 3 日和 1 1 1 6 6 6 日的票据

【评测用例规模与约定】

  对于 100 % 100\% 100% 的评测用例, 1 ≤ N ≤ 1000 , 1 ≤ M ≤ 5000 , 1 ≤ K ≤ 50 , 1 ≤ m i ≤ 12 , 1 ≤ d i ≤ 31 , 1 ≤ v i ≤ 400 1 ≤ N ≤ 1000, 1 ≤ M ≤ 5000, 1 ≤ K ≤ 50, 1 ≤ m_i ≤ 12, 1 ≤ d_i ≤ 31, 1 ≤ v_i ≤ 400 1N1000,1M5000,1K50,1mi12,1di31,1vi400
  日期保证合法。


#include <stdio.h>
#include <algorithm>
#include <utility>

const int max_N = 1e3 + 1, max_M = 5 * 1e3 + 1;

const int offset[]{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

std::pair<int, int> bill[max_N];

bool dp[max_N][max_M]{1};

int N, M, K, m, d, v;

int main() {
    scanf("%d %d %d", &N, &M, &K);
    for (int i = 1; i <= N; ++i)
        scanf("%d %d %d", &m, &d, &v),
        bill[i] = {offset[m] + d, v};
    std::sort(bill + 1, bill + N + 1);
    for (int i = 1, k = 0; i <= N; ++i) {
        while (bill[i].first - bill[k + 1].first >= K) ++k;
        for (int j = M; j >= bill[i].second; --j)
            dp[i][j] = dp[i - 1][j] | dp[k][j - bill[i].second];
        for (int j = bill[i].second - 1; ~j; --j) dp[i][j] = dp[i - 1][j];
    }
    for (int i = M; ~i; --i)
        if (dp[N][i]) {
            printf("%d", i);
            return 0;
        }
}

  模板 × 4 \times 4 ×4,还是 0 0 0 - 1 1 1 背包。


试题 G: 故障

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20


【问题描述】

  在软件或系统开发中,我们会遇到各种各样的故障。为了从故障现象反推故障原因,工程师们会总结一种叫做相关性矩阵的二维表格,来表示故障原因与故障现象之间的关系。比如 : :

+ − − − + − − − + − − − + − − − + − − − + − − − + \scriptstyle{+---+---+---+---+---+---+} +−−−+−−−+−−−+−−−+−−−+−−−+
∣ ∣      1     ∣      2     ∣      3     ∣      4     ∣      5     ∣ \scriptstyle\: |\quad\quad| \textstyle{\ \ \: 1\ \ \ }\scriptstyle| \textstyle{\ \ \: 2\ \ \ }\scriptstyle|\textstyle{\ \ \: 3\ \ \ }\scriptstyle|\textstyle{\ \ \: 4\ \ \ }\scriptstyle|\textstyle{\ \ \: 5\ \ \ }\scriptstyle|   1     2     3     4     5   
+ − − − + − − − + − − − + − − − + − − − + − − − + \scriptstyle{+---+---+---+---+---+---+} +−−−+−−−+−−−+−−−+−−−+−−−+
∣      A     ∣ ∣      x      ∣      x      ∣      x      ∣ ∣ \scriptstyle\:| \textstyle{\ \: \: \small\rm A\: \: \: }\scriptstyle|\qquad\scriptstyle| \textstyle{\ \ \: \rm x\: \ \ }\scriptstyle|\textstyle{\ \ \: \rm x\: \ \ }\scriptstyle|\textstyle{\ \ \: \rm x\: \ \ }\scriptstyle|\qquad\scriptstyle|  A  x    x    x  
+ − − − + − − − + − − − + − − − + − − − + − − − + \scriptstyle{+---+---+---+---+---+---+} +−−−+−−−+−−−+−−−+−−−+−−−+
∣      B     ∣     x      ∣ ∣      x      ∣ ∣ ∣ \scriptstyle\:|\textstyle{\ \: \: \small\rm B\: \: \: }\scriptstyle|\textstyle{\ \ \ \rm x \: \ \ }\scriptstyle|\qquad|\textstyle{\ \ \: \rm x\:\ \ }\scriptstyle|\qquad|\qquad|  B   x    x  
+ − − − + − − − + − − − + − − − + − − − + − − − + \scriptstyle{+---+---+---+---+---+---+} +−−−+−−−+−−−+−−−+−−−+−−−+
∣      C     ∣ ∣ ∣ ∣      x      ∣      x     ∣ \scriptstyle\:|\textstyle{\ \: \: \small \rm C\: \: \: }\scriptstyle|\qquad|\qquad|\qquad\scriptstyle|\textstyle{\ \ \: \rm x \: \ \ }\scriptstyle|\textstyle{\ \ \:\rm x \ \ \ }\scriptstyle|  C  x    x   
+ − − − + − − − + − − − + − − − + − − − + − − − + \scriptstyle{+---+---+---+---+---+---+} +−−−+−−−+−−−+−−−+−−−+−−−+

  其中每行表示一种故障原因,每一列表示一种故障现象。该矩阵表示故障原因 A A A 可能产生故障现象 2 、 3 、 4 2、3、4 234,故障原因 B B B 可能产生故障现象 1 、 3 1、3 13

  在实际开发过程中,如果出现了故障原因,工程师就可以根据故障现象,去计算每种故障原因产生的概率,并按照概率大小对故障原因进行排查,以达到快速定位故障原因的目的。

  现在,我们假设系统开发中同一时间只会出现一种故障原因,并且故障原因引起各故障现象是独立事件。举个例子来说 : :

  假设系统现在发生了故障原因 A A A,有 1 3 \frac 13 31 的概率出现故障现象 2 2 2,有 1 4 \frac 14 41 的概率出现故障现象 3 3 3,有 1 2 \frac 12 21 的概率出现故障现象 4 4 4。由于 3 3 3 种现象是独立发生的,因此有 1 2 × 3 × 4 \frac 1{2\times3\times4} 2×3×41 的概率同时出现故障 2 、 3 、 4 2、3、4 234

  约定若相关性矩阵中没有 ‘ x ’ \rm ‘x’ ‘x’ 记号,则表示该故障原因一定不会产生某故障现象,比如故障原因 A A A,一定不会产生故障现象 1 1 1

  根据历史经验数据,我们统计得到了每一种故障原因出现的概率以及每一种故障原因对应的故障现象产生概率。

  现在已知系统出现的故障现象,求问各个故障原因发生的概率。

【输入格式】

  第 1 1 1 : : 2 2 2 个正整数 N , M N, M N,M N N N 表示故障原因的个数(编号 1 ⋯ N 1 \cdots N 1N), M M M 表示故障现象的个数(编号 1 ⋯ M 1 \cdots M 1M

  第 2 2 2 : : N N N 个整数,第 i i i 个数表示故障原因 i i i 产生的概率 P i P_i Pi.

  第 3 ⋯ N + 2 3 \cdots N + 2 3N+2 : : 每行 M + 1 M + 1 M+1 个整数,第 i + 1 i + 1 i+1 行第 j j j 个整数 P i j P_{i j} Pij 表示故障原因 i i i 出现故障现象 j j j 的概率(百分比) . . .

  第 N + 3 N + 3 N+3 : : 1 1 1 个正整数 K K K,表示目前出现的故障现象数量

  第 N + 4 N + 4 N+4 : : K K K 个正整数,依次为当前出现的故障现象编号,不会重复

【输出格式】

  第 1 ⋯ N 1 \cdots N 1N : : 按概率从高到低输出每种故障原因及其可能的概率,若出现概率相同则优先输出编号小的故障原因。第 1 1 1 个数为故障原因编号,第 2 2 2 个数为故障概率(百分比),保留 2 2 2 位小数。

【样例输入】

3 5
30 20 50
0 50 33 25 0
30 0 35 0 0
0 0 0 25 60
1
3

【样例输出】

2 56.89
1 43.11
3 0.00

【评测用例规模与约定】

  对于所有测试用例, 1 ≤ N ≤ 40 , 1 ≤ M ≤ 20 , 0 ≤ P i ≤ 100 , Σ ( P i ) = 100 , 0 ≤ P i j ≤ 100 1 ≤ N ≤ 40, 1 ≤ M ≤ 20, 0 ≤ P_i ≤ 100,\Sigma(P_i) = 100,0 ≤ P_{i j} ≤ 100 1N40,1M20,0Pi100Σ(Pi)=1000Pij100


贝叶斯定理


#include <stdio.h>
#include <algorithm>
#include <math.h>

int n, m, k, P[40][21];

struct node {
    double p;
    int i;
    inline bool operator<(const node &nd) const {
        return p == nd.p ? i < nd.i : p > nd.p;
    }
} ans[40];

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 0; i < n; ++i)
	    scanf("%d", P[i]), ans[i].i = i + 1;
	for (int i = 0; i < n; ++i)
	    for (int j = 1; j <= m; ++j)
	        scanf("%d", &P[i][j]), P[i][j] = 100 - P[i][j];
	scanf("%d", &k);
	for (int g = 0, j; g < k; ++g) {
	    scanf("%d", &j);
	    for (int i = 0; i < n; ++i)
	        P[i][j] = 100 - P[i][j];
	}
	double tmp = 0;
	for (int i = 0; i < n; ++i) {
	    ans[i].p = *P[i] / 100.0;
	    for (int j = 1; j <= m; ++j)
	        ans[i].p *= P[i][j] / 100.0;
	    tmp += ans[i].p;
	}
	std::sort(ans, ans + n);
	for (int i = 0; i < n; ++i)
	    printf("%d %.2lf\n", ans[i].i, !tmp ? 0.0 : 100 * ans[i].p / tmp);
}

  全概率公式与贝叶斯公式裸题,注意判一下 P ( B ) P(B) P(B) 0 0 0 的情况就行了。

  模板 × 5 \times 5 ×5


试题 H: 机房

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20


【问题描述】

  这天,小明在机房学习。

  他发现机房里一共有 n n n 台电脑,编号为 1 1 1 n n n,电脑和电脑之间有网线连接,一共有 n − 1 n−1 n1 根网线将 n n n 台电脑连接起来使得任意两台电脑都直接或者间接地相连。

  小明发现每台电脑转发、发送或者接受信息需要的时间取决于这台电脑和多少台电脑直接相连, 而信息在网线中的传播时间可以忽略。比如如果某台电脑用网线直接连接了另外 d d d 台电脑,那么任何经过这台电脑的信息都会延迟 d d d 单位时间 (发送方和接收方也会产生这样的延迟,当然如果发送方和接收方都是同一台电脑就只会产生一次延迟)。

  小明一共产生了 m m m 个疑问 : : 如果电脑 u i u_i ui 向电脑 v i v_i vi 发送信息,那么信息从 u i u_i ui 传到 v i v_i vi 的最短时间是多少?

【输入格式】

  输入共 n + m n + m n+m 行,第一行为两个正整数 n , m n,m n,m

  后面 n − 1 n−1 n1 行,每行两个正整数 x , y x,y x,y 表示编号为 x x x y y y 的两台电脑用网线直接相连。

  后面 m m m 行,每行两个正整数 u i , v i u_i,v_i ui,vi 表示小明的第 i i i 个疑问。

【输出格式】

  输出共 m m m 行,第 i i i 行一个正整数表示小明第 i i i 个疑问的答案。

【样例输入】

4 3
1 2
1 3
2 4
2 3
3 4
3 3

【样例输出】

5
6
1

【样例说明】

  这四台电脑各自的延迟分别为 2 , 2 , 1 , 1 2,2,1,1 2,2,1,1
  对于第一个询问,从 2 2 2 3 3 3 需要经过 2 , 1 , 3 2,1,3 2,1,3,所以时间和为 2 + 2 + 1 = 5 2 + 2 + 1 = 5 2+2+1=5
  对于第二个询问,从 3 3 3 4 4 4 需要经过 3 , 1 , 2 , 4 3,1,2,4 3,1,2,4,所以时间和为 1 + 2 + 2 + 1 = 6 1+2+2+1 =6 1+2+2+1=6
  对于第三个询问,从 3 3 3 3 3 3 只会产生一次延迟,所以时间为 1 1 1

【评测用例规模与约定】

  对于 30 % 30\% 30% 的数据,保证 n , m ≤ 1000 n,m≤1000 n,m1000
  对于 100 % 100\% 100% 的数据,保证 n , m ≤ 100000 n,m≤100000 n,m100000


Tarjan


#include <stdio.h>

const int N = 100009;

int linked[N], query[N], ans[N];

int next[N << 2], ver[N << 2], idx[N << 2], cur = 0;

int n, m, x, y, fa[N], val[N], parent[N];

bool visited[N];

int find(int rt) { return parent[rt] == rt ? rt : (parent[rt] = find(parent[rt])); }

void tarjan(int u) {
    val[u] += val[fa[u]];
    visited[u] = 1;
    parent[u] = u;
    for (int i = linked[u]; i; i = next[i])
        if (!visited[ver[i]])
            fa[ver[i]] = u, tarjan(ver[i]), parent[ver[i]] = u;
    for (int i = query[u]; i; i = next[i])
        if (visited[ver[i]])
            ans[idx[i]] = val[u] + val[ver[i]] - val[find(ver[i])] - val[fa[find(ver[i])]];
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i < n; ++i) {
        scanf("%d %d", &x, &y);
        ++cur, ++val[x], next[cur] = linked[x], linked[x] = cur, ver[cur] = y;
        ++cur, ++val[y], next[cur] = linked[y], linked[y] = cur, ver[cur] = x;
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d %d", &x, &y);
        ++cur, idx[cur] = i, next[cur] = query[x], query[x] = cur, ver[cur] = y;
        ++cur, idx[cur] = i, next[cur] = query[y], query[y] = cur, ver[cur] = x;
    }
    tarjan(1);
    for (int i = 0; i < m; ++i) printf("%d\n", ans[i]);
}

   L C A \rm LCA LCA 裸题,模板 × 6 \times 6 ×6


试题  I: 齿轮

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 25 25 25


【问题描述】

  这天,小明在组装齿轮。

  他一共有 n n n 个齿轮,第 i i i 个齿轮的半径为 r i r_i ri,他需要把这 n n n 个齿轮按一定顺序从左到右组装起来,这样最左边的齿轮转起来之后,可以传递到最右边的齿轮,并且这些齿轮能够起到提升或者降低转速 (角速度) 的作用。

在这里插入图片描述

  小明看着这些齿轮,突然有 Q Q Q 个疑问:能否按一定顺序组装这些齿轮使得最右边的齿轮的转速是最左边的齿轮的 q i q_i qi 倍?

【输入格式】

  输入共 Q + 2 Q + 2 Q+2 行,第一行为两个正整数 n , Q n, Q n,Q,表示齿轮数量和询问数量。

  第二行为 n n n 个正整数 r 1 , r 2 , . . . , r n r_1,r_2, ...,r_n r1,r2,...,rn,表示每个齿轮的半径。

  后面 Q Q Q 行,每行一个正整数 q i q_i qi 表示询问。

【输出格式】

   Q Q Q 行,对于每个询问,如果存在至少一种组装方案满足条件,输出 ‘ Y E S ‘ \rm ‘YES‘ ‘YES‘,否则输出 ‘ N O ‘ \rm ‘NO‘ ‘NO‘

【样例输入】

5 3
4 2 3 3 1
2
4
6

【样例输出】

YES
YES
NO

【样例说明】

  询问 1 1 1 方案之一 : : 2   3   3   4   1 2\ 3\ 3\ 4\ 1 2 3 3 4 1
  询问 2 2 2 方案之一 : : 4   2   3   3   1 4\ 2\ 3\ 3\ 1 4 2 3 3 1
  询问 3 3 3 没有方案

【评测用例规模与约定】

  对于 15 % 15\% 15% 的数据,保证 n , Q ≤ 100 n, Q ≤ 100 n,Q100
  对于 30 % 30\% 30% 的数据,保证 n , Q ≤ 2000 n, Q ≤ 2000 n,Q2000
  对于 100 % 100\% 100% 的数据,保证 n , Q ≤ 2 × 1 0 5 ; a i , q i ≤ 2 × 1 0 5 n, Q ≤ 2 × 10^5; a_i, q_i ≤ 2 × 10^5 n,Q2×105;ai,qi2×105


  容易发现 n n n 个齿轮按一定顺序组装起来,最左齿轮与最右齿轮之间,转速的关系与这一序列齿轮中间 2 ∼ n − 1 2 \sim n-1 2n1 个无关。

  于是问题就被转变为,对于每一个 q q q,是否存在一对 i , j i,j i,j,满足 r i = q r j r_i = qr_j ri=qrj,看一眼数据范围,直接倍数法离线求解了。

#include <stdio.h>

const int N = 200000;

bool g[N + 1], q[N + 1];

int n, r, Q;

int main() {
    scanf("%d %d", &n, &Q);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &r);
        if (g[r]) q[1] = 1;
        g[r] = 1;
    }
    for (int i = 1; i <= N; ++i)
        if (g[i])
            for (int j = 2; i * j <= N; ++j)
                if (g[i * j]) q[j] = 1;
    for (int i = 0; i < Q; ++i)
        scanf("%d", &r), puts(q[r] ? "YES" : "NO");
}

试题 J: 搬砖

时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 25 25 25


【问题描述】

  这天,小明在搬砖。

  他一共有 n n n 块砖,他发现第 i i i 砖的重量为 w i w_i wi,价值为 v i v_i vi。他突然想从这些砖中选一些出来从下到上堆成一座塔,并且对于塔中的每一块砖来说,它上面所有砖的重量和不能超过它自身的价值。

  他想知道这样堆成的塔的总价值(即塔中所有砖块的价值和)最大是多少。

【输入格式】

  输入共 n + 1 n + 1 n+1 行,第一行为一个正整数 n n n,表示砖块的数量。

  后面 n n n 行,每行两个正整数 w i , v i w_i, v_i wi,vi 分别表示每块砖的重量和价值。

【输出格式】

  一行,一个整数表示答案。

【样例输入】

5
4 4
1 1
5 2
5 5
4 3

【样例输出】

10

【样例说明】

  选择第 1 、 2 、 4 1、2、4 124 块砖,从上到下按照 2 、 1 、 4 2、1、4 214 的顺序堆成一座塔,总价值为 4 + 1 + 5 = 10 4 + 1 + 5 = 10 4+1+5=10

【评测用例规模与约定】

  对于 20 % 20\% 20% 的数据,保证 n ≤ 10 n ≤ 10 n10
  对于 100 % 100\% 100% 的数据,保证 n ≤ 1000 ; w i ≤ 20 ; v i ≤ 20000 n ≤ 1000; w_i ≤ 20; v_i ≤ 20000 n1000;wi20;vi20000


背包 DP


#include <stdio.h>
#include <algorithm>

inline int max(int a, int b) { return a > b ? a : b; }

struct goods{
    int w, v;

    inline bool operator< (const goods gds) const { return w + v < gds.w + gds.v; }
} brick[20000];

int n, ans, dp[200001];

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
        scanf("%d %d", &brick[i].w, &brick[i].v);
    std::sort(brick, brick + n);
    for (int i = 0; i < n; ++i)
        for (int j = brick[i].v; j >= 0; --j)
            if (dp[j] || !j) dp[j + brick[i].w] = max(dp[j + brick[i].w], dp[j] + brick[i].v);
    for (int i = brick[n - 1].v + brick[n - 1].w; i >= 0; --i) ans = max(ans, dp[i]);
    printf("%d", ans);
}
  • 21
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值