2021年第⼗⼆届蓝桥杯CC++A 组题解
A 卡⽚
问题描述
⼩蓝有许多张卡⽚,每张卡⽚上的数字都是0到9。他想从1开始拼正整数,每拼⼀个数,卡⽚就保存起来,不能再拼其他数。现在他⼿⾥的卡⽚0到9各有2021张,他想知道⾃⼰能从1拼到多少?
思路
对于每⼀个能拼的数x,统计x的每⼀位分别是什么,让x从1开始增加,直到需要⽤的某⼀卡⽚数量为0为⽌。模拟即可。代码
答案
3181
B 直线
问题描述
⼆维平⾯给出20×21个点,它们能构成多少条不同的直线?
思路
两点构成⼀直线,因此,枚举所有的点对,420个点共构成419+418+417+……+1对,即(1+419)×419÷2=87990对。除去这87990条直线中重复的直线,就是正确答案。
数据结构set具有去重的功能,因此我们可以⽤set存储结构体node,node表⽰的是直线⼀般式的三个参数,即ax+by+c=0中的a,b,c。不⽤点斜式是因为计算斜率k会带来较⼤误差。
当然,a,b,c⼀定是互素的,即gcd(a,b,c)==1,才能保证直线不重复。直接除以三个数绝对值的gcd把三元组化为最简形式即可。最后输出set.pair()。
推导  1. #include <iostream >  2. #include <algorithm >  3. using  namespace  std ;  4. int  card [10];  5. int  main ()  6. {  7.    for  (int  i = 0; i <= 9; i ++)card [i ] = 2021;  8.    bool  flag = true ;  9.    int  num = 1;  10.    for  (num = 1; flag ; num ++) {  11.        int  x = num ;  12.        while  (x ) {  13.            int  cur = x % 10;  14.            if  (card [cur ])card [cur ]--;  15.            else  { flag = false ; break ; }  16.            x /= 10;  17.        }  18.    }  19.    cout << num - 2 << endl ;  20.    return  0;  21. }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
点p1(x1,y1),点p2(x2,y2),斜率k=(y1-y2)/(x1-x2)。点斜式⽅程y-y1=k(x-x1),即(y-y1)×(x1-x2)=(y1-y2)×(x-x1),得yx1-x1y1-yx2+x2y1=xy1-xy2-x1y1+x1y2,故(y1-y2)x+(x2-x1)y+(x1y2-x2y1)=0。
其中a=y1-y2,b=x2-x1,c=x1y2-x2y1。
如果插⼊的类型是⾃定义的,不是基本类型,需要重载<;运算符,给⾃定义类型⼀个排序准则。
以上的做法是为了完全避免精度问题造成的影响,才将点斜式转化为⼀般式表⽰,否则答案将会千奇百怪。
代码
答案
40257
C 货物摆放
题⽬描述
有n箱货物,长宽⾼的⽅向上分别堆L、W、H的货物,满⾜n=L×W×H。给定n=2021041820210418,总共有多少种堆放货物的⽅案?
思路  1. #include <iostream >  2. #include <algorithm >  3. #include <set >  4. using  namespace  std ;  5. struct  node1 {  6.    int  a , b , c ;  7.    friend  bool  operator  < (const  node1 &A , const  node1 &B )
{  8.        if  (A .a == B .a )return  A .b == B .b ? A .c < B .c : A .b < B .b ;  9.        return  A .a < B .a ;  10.    }  11. }line [87991];  12. struct  node2 {  13.    int  x , y ;  14. }point [421];  15. set <node1> s ;  16. int  gcd (int  a , int  b ) {  17.    return  b == 0 ? a : gcd (b , a %b );  18. }  19. int  main ()  20. {  21.    int  cnt = 0;  22.    for  (int  i = 0; i < 20; i ++) {  23.        for  (int  j = 0; j < 21; j ++) {  24.            point [cnt ++] = { i ,j };  25.        }  26.    }  27.    for  (int  i = 0; i < cnt ; i ++) {  28.        for  (int  j = i + 1; j < cnt ; j ++) {  29.            int  a = point [i ].y - point [j ].y ;  30.            int  b = point [j ].x - point [i ].x ;  31.            int  c = point [i ].x *point [j ].y - point [j ].x *point [i ].y ;  32.            int  GCD = gcd (abs (a ), gcd (abs (b ), abs (c )));  33.            a /= GCD , b /= GCD , c /= GCD ;  34.            s .insert ({ a ,b ,c });  35.        }  36.    }  37.    cout << s .size () << endl ;  38.    return  0;  39. }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
先求n的所有因数。已知所有分解出的因数,再让L分别取这些因数,计算每⼀个L取得的因数所对应的W×H,即n/L。因此,只需要再计算n/L的所有因数,并按照W与H的不同位置来求得W×H的所有取值⽅案,就可以对应到每⼀个L取得的因数。累加即得答案。代码
答案
2430
D 路径
问题描述
两结点数字绝对值之差⼩于等于21有边,边权为两数的最⼩公倍数。计算结点1到2021的最短路径长度。
思路
先模拟建图,可以⽤静态邻接表。
解法⼀:暴⼒跑最短路弗洛伊德算法
⽤时17秒。测得本机在⼀秒内可以完成的计算量为4.87×10^8。
代码⼀1. #include <iostream >  2. #include <algorithm >  3. #define int  long  long  4. using  namespace  std ;  5. int  n = 2021041820210418;  6. int  ans [100000];  7. int  cal (int  x ) {  8.    int  t = 0;  9.    for  (int  i = 1; i *i <= x ; i ++) {  10.        if  (x %i == 0) {  11.            if  (i *i == x )t ++;  12.            else  t += 2;  13.        }  14.    }  15.    return  t ;  16. }  17. signed  main ()  18. {  19.    int  cnt = 0;  20.    for  (int  i = 1; i *i <= n ; i ++) {  21.        if  (n %i == 0) {  22.            if  (i *i == n )ans [++cnt ] = i ;  23.            else  ans [++cnt ] = i , ans [++cnt ] = n / i ;  24.        }  25.    }  26.    int  res = 0;  27.    for  (int  i = 1; i <= cnt ; i ++) {  28.        res += cal (ans [i ]);  29.    }  30.    cout << res << endl ;  31.    return  0;  32. }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
解法⼆:线性DP
状态表⽰d[i]:从结点1到i的所有路径
集合划分:所有能从i的上⼀个结点j转移到结点i的路径,且j的取值范围是i-21到i-1,且j>=1。
属性:最⼩值
状态计算:d[i]=min(d[i],d[j]+g[i][j])
初始化:d[1]=0
代码⼆3. #define inf 0x3f3f3f3f  4. using  namespace  std ;  5. int  g [2022][2022];  6. int  gcd (int  a , int  b ) {  7.    return  b == 0 ? a : gcd (b , a %b );  8. }  9. int  lcm (int  a , int  b ) {  10.    return  a *b / gcd (a , b );  11. }  12. int  main ()  13. {  14.    for  (int  i = 1; i <= 2021; i ++) {  15.        for  (int  j = i + 1; j <= 2021; j ++) {  16.            if  (abs (i - j ) <= 21)g [i ][j ] = g [j ][i ] = lcm (i , j );  17.            else  g [i ][j ] = g [j ][i ] = inf ;  18.        }  19.    }  20.    for  (int  k = 1; k <= 2021; k ++) {  21.        for  (int  i = 1; i <= 2021; i ++) {  22.            for  (int  j = 1; j <= 2021; j ++) {  23.                if  (g [i ][k ] + g [k ][j ] < g [i ][j ]) {  24.                    g [i ][j ] = g [i ][k ] + g [k ][j ];  25.                }  26.            }  27.        }  28.    }  29.    cout << g [1][2021] << endl ;  30.    return  0;  31. }
3
4
5
6
7
8
cstring转为int9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
答案
10266837
E 回路计数
题⽬描述
编号从1到21有21个结点,若两结点上的数互质,则两结点间存在⼀条⽆向边。初始时⼩蓝在第⼀个结点,若要恰好访问每个结点⼀次,并最终返回第⼀个结点,问存在多少种不同的访问⽅案?
思路
求最短哈密顿回路,需要⽤状态压缩DP计算。
分别重新编号为0到20,转化为标准哈密顿模型。
状态表⽰f[i][j]:所有从起点0⾛到终点j,⾛过的所有点对应的状态压缩表⽰为i的所有路径。
属性:从0到j的所有路径的数量
集合划分:根据倒数第⼆个点是哪个点来分类,即最后⼀步是从第k个点⾛到第j个点,且k可能为除j之外的任意⼀个点。
状态计算:f[i][j]=Σf[i-(1<<j)][k]
转移条件:状态i的第j位已经⾛到,i-(1<<j)这⼀状态的第k位已经⾛到,且从k到j的路径存在。
初始化:f[1][0]=1
结果:可能最终是从编号1到20的任意⼀个结点返回的0结点。
代码  3. #include <cstring >  4. using  namespace  std ;  5. int  g [2022][2022];  6. int  gcd (int  a , int  b ) {  7.    return  b == 0 ? a : gcd (b , a %b );  8. }  9. int  lcm (int  a , int  b ) {  10.    return  a *b / gcd (a , b );  11. }  12. int  d [2022];  13. int  main ()  14. {  15.    for  (int  i = 1; i <= 2021; i ++) {  16.        for  (int  j = i - 1; j >= i - 21 && j >= 1; j --) {  17.            g [j ][i ] = lcm (i , j );  18.        }  19.    }  20.    memset (d , 0x3f , sizeof (d ));  21.    d [1] = 0;  22.    for  (int  i = 1; i <= 2021; i ++) {  23.        for  (int  j = i - 1; j >= i - 21 && j >= 1; j --) {  24.            d [i ] = min (d [i ], d [j ] + g [j ][i ]);  25.        }  26.    }  27.    cout << d [2021] << endl ;  28.    return  0;  29. }
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。