2024蓝桥杯省赛C/C++大学B组 题解

2024蓝桥杯省赛C/C++大学B组

A 握手问题(5分)

小蓝组织了一场算法交流会议,总共有 50 人参加了本次会议。在会议上,大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 7 个人,这 7 人彼此之间没有进行握手 (但这 7 人与除这 7 人以外的所有人进行了握手)。请问这些人之间一共进行了多少次握手?

注意 A 和 B 握手的同时也意味着 B 和 A 握手了,所以算作是一次握手。

【分析】最后答案为 1204,两种思考方式

  • 有 43 人两两握手,7人会和这 43人握手,所以答案为 C 43 2 + 7 × 43 C_{43}^2 + 7\times 43 C432+7×43
  • 用 50 个人握手的总方案数,减去 7 个人彼此没有握手的方案数,所以答案为 C 50 2 − C 7 2 C_{50}^2 - C_7^2 C502C72

B 小球反弹(5分)

有一长方形,长为 343720 单位长度,宽为 233333 单位长度。 在其内部左上角顶点有一小球 (无视其体积),其初速度如图所示且保持运动速率不变,分解到长宽两个方向上的速率之比为 dx:dy=15:17。 小球碰到长方形的边框时会发生反弹,每次反弹的入射角与反射角相等,因此小球会改变方向且保持速率不变(如果小球刚好射向角落,则按入射方向原路返回)。从小球出发到其第一次回到左上角顶点这段时间里,小球运动的路程为多少单位长度?答案四舍五入保留两位小数。

在这里插入图片描述

【分析】画图分析,每次反射实际上可以转换为沿着该反射边的对称变化,通过推导可以知道 横纵轴分别需要的方格数量,由于求第一次反射的答案,所以需要约分,又因为需要反射到起点,所以计算的结果需要 × 2 \times 2 ×2, 最后答案为 1100325199.77

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;

ll gcd(ll a,ll b){
    
    
    return b? gcd(b, a%b) : a;
}
int main(){
    
    
    ll dx= 15, dy=17;
    ll a=343720, b=233333;
    ll n = dx * b, m = dy *a;
    ll d = gcd(n, m);
    n /= d, m /= d;
    double x = a*n, y = b*m;
    double s = 2*sqrt(x*x + y*y);
    cout<<fixed<<setprecision(4)<<s<<endl;
    return 0; 
}

C 好数(10分)

一个整数如果按从低位到高位的顺序,奇数位 (个位、百位、万位 ⋯ ) 上的数字是奇数,偶数位 (十位、千位、十万位 ⋯ ) 上的数字是偶数,我们就称之为 “好数”。

给定一个正整数 N,请计算从 1 到 N 一共有多少个好数。

【分析】 循环枚举 1 到 N,对每个数判断是不是好数即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, INF = 0x3f3f3f3f;

bool check(int n) {
    
    
    int t = n, f = 1;
    while (t) {
    
    
        int x = t % 10;
        if (x % 2 != f) return 0;
        t /= 10, f = !f;
    }
    return 1;
}
int main() {
    
    
    int n, ans = 0; cin >> n;
    for (int i = 1; i <= n; i++)  ans += check(i);
    cout << ans << endl;
    return 0;
}

D R 格式(10分)

给定一个整数 n,一个大于0的浮点数d,将 d 转换为 R 格式整数的做法是:将浮点数乘以 2 n 2^n 2n,然后四舍五入到最接近的整数。

  • 对于 50 % 50\% 50% 的数据:$1\le n\le 10,1\le $ 将d视为字符串时的长度 ≤ 15 \le 15 15
  • 对于 100 % 100\% 100% 的数据:$1\le n\le 1000,1\le $ 将d视为字符串时的长度 ≤ 1024 \le 1024 1024;保证d是小数,即包含小数点。

【分析】 d = r o u n d ( d ∗ 2 n ) d=round(d*2^n) d=round(d2n),对于小数据可以直接用公式计算,但是对于较大数据发现需要使用高精度乘以低精度,为了使用整数高精度可以将 d = d × 1 0 x d=d\times 10^x d=d×10x 转为整数,之后计算的结果除以 1 0 x 10^x 10x

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, INF = 0x3f3f3f3f;

int main1(){
    
    // score: 50%
    int n; double d; cin>>n>>d;
    // ll s = round(d * pow(2.0, n));
    ll s = d * pow(2.0, n) + 0.5; // 四舍五入
    cout<<s<<endl;    
    return 0; 
}
vector<int> mul(vector<int>&a, int b){
    
    
    vector<int> c; int t=0;
    for(int i=0; i<a.size()||t; i++){
    
    
        if(i<a.size()) t += a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(c.size()>1 && c.back()==0) c.pop_back();
    return c;
}
int main(){
    
    
    int n; string d; cin>>n>>d;
    int id = d.find('.');
    int x = d.size() - id - 1;
    d.erase(id, 1);// d = d * 10 ^ x

    vector<int> a;
    for (auto u : d) a.push_back(u - '0');
    reverse(a.begin(), a.end());// 初始反转,后续就一直是反的
    for(int i=1; i<=n; i++) a = mul(a, 2);
    
    string ans = "";
    a[x] += a[x - 1] >= 5; // 四舍五入
    for (int i = x, t = 0; i < a.size() || t; i++) {
    
    
        if (i < a.size()) t += a[i];
        ans += t % 10 + '0';
        t /= 10;
    }
    reverse(ans.begin(), ans.end());
    cout << ans << endl;
    return 0;
}

E 宝石组合(15分)

小蓝共找到了 N 枚宝石,第 i i i 枚宝石的“闪亮度”属性值为 H i H_i Hi。小蓝将会从这 N 枚宝石中选出三枚进行组合,组合之后的精美程度 S 可以用以下公式来衡量:

S = H a H b H c × L C M ( H a , H b , H c ) L C M ( H a , H b ) × L C M ( H a , H c ) × L C M ( H b , H c ) S = H_a H_b H_c \times \frac{LCM(H_a, H_b, H_c)}{LCM(H_a, H_b) \times LCM(H_a, H_c) \times LCM(H_b, H_c)} S=HaHbHc×LCM(Ha,Hb)×LCM(Ha,Hc)×LCM(Hb,Hc)LCM(Ha,Hb,Hc)

其中 LCM 表示的是最小公倍数函数。小蓝想要使得三枚宝石组合后的精美程度 S 尽可能的高,请你帮他找出精美程度最高的方案。
如果存在多个方案 S 值相同,优先选择按照 H H H 值升序排列后字典序最小的方案。

  • 对于 30% 的数据: 3 ≤ N ≤ 100 , 1 ≤ H i ≤ 1000 3 \le N \le 100, 1 \le H_i \le 1000 3N100,1Hi1000
  • 对于 60% 的数据: 3 ≤ N ≤ 2000 3 \le N \le 2000 3N2000
  • 对于 100% 的数据: 3 ≤ N ≤ 1 0 5 , 1 ≤ H i ≤ 1 0 5 3 \le N \le 10^5, 1 \le H_i \le 10^5 3N105,1Hi105

【分析】

  • 对于 30% 的数据,可以直接枚举 a,b,c,复杂度 O ( n 3 × l o g ) O(n^3 \times log) O(n3×log);
  • 对于原公式化简(过程见下文),可以得到 S = g c d ( H a , H b , H c ) S=gcd(H_a,H_b,H_c) S=gcd(Ha,Hb,Hc),问题转变为在数据中选择三个数字,使得其 gcd 最大。
    如果直接枚举,复杂度并不会有太多的优化,于是考虑正难则反。
    S 一定是在 [ 1 , m i n { h i } ] [1, min\{h_i\}] [1,min{ hi}],考虑对每个数据求一下因子,构造这样的情况。
1 : 1,2,3,4,9   ---- 因子含有 1 的数据有 : 1,2,3,4,9  
2 : 2,4         ---- 因子含有 2 的数据有 : 2,4
3 : 3,9         ---- 因子含有 3 的数据有 : 3,9
4 : 4           ---- 因子含有 4 的数据有 : 4
9 : 9           ---- 因子含有 9 的数据有 : 9

如果需要 S = g c d ( H a , H b , H c ) S=gcd(H_a,H_b,H_c) S=gcd(Ha,Hb,Hc) 最大,那么必然因子含有 S 的数据个数不少于 3 个,于是考虑对所有的数据进行因子的处理,然后找到数据数量不少于 3 的最大因子的最小字典序即可。

证明: a × b × c × lcm ( a , b , c ) l c m ( a , b ) × l c m ( a , c ) × l c m ( b , c ) = g c d ( a , b , c ) . 设  a = ∏ i = 1 n p i α i , b = ∏ i = 1 n p i β i , c = ∏ i = 1 n p i γ i ,  其中  p i  为质数。 左边 = ∏ i = 1 n p i α i × ∏ i = 1 n p i β i × ∏ i = 1 n p i γ i × ∏ i = 1 n p i max ⁡ ( α i , β i , γ i ) ∏ i = 1 n p i max ⁡ ( α i , β i ) × ∏ i = 1 n p i max ⁡ ( β i , γ i ) × ∏ i = 1 n p i max ⁡ ( α i , γ i ) = ∏ i = 1 n ( p i α i + β i + γ i × p i max ⁡ ( α i , β i , γ i ) p i max ⁡ ( α i , β i ) + max ⁡ ( α i , γ i ) + max ⁡ ( β i , γ i ) ) = ∏ i = 1 n p i α i + β i + γ i + max ⁡ ( α i , β i , γ i ) − max ⁡ ( α i , β i ) − max ⁡ ( α i , γ i ) − max ⁡ ( β i , γ i ) = ∏ i = 1 n p i min ⁡ ( α i , β i , γ i ) = g c d ( a , b , c ) \begin{align*} \text{证明:} &a\times b\times c\times \frac{\text{lcm}(a,b,c)} {lcm(a,b)\times lcm(a,c)\times lcm(b,c)} = gcd(a,b,c).\\ \text{设 } a &= \prod_{i=1}^n{p_{i}^{\alpha_i}}, b = \prod_{i=1}^n{p_{i}^{\beta_i}}, c = \prod_{i=1}^n{p_{i}^{\gamma_i}}, \text{ 其中 } p_i \text{ 为质数。}\\ \text{左边} &= \prod_{i=1}^{n}{p_i}^{\alpha_i}\times \prod_{i=1}^{n}{p_i}^{\beta_i}\times \prod_{i=1}^{n}{p_i}^{\gamma_i} \times \frac{\prod_{i=1}^n{p_i^{\max(\alpha_i,\beta_i,\gamma_i)}}} {\prod_{i=1}^n{p_i^{\max(\alpha_i,\beta_i)}}\times \prod_{i=1}^n{p_i^{\max(\beta_i,\gamma_i)}}\times \prod_{i=1}^n{p_i^{\max(\alpha_i,\gamma_i)}}}\\ &=\prod_{i=1}^{n}{({p_i}^{\alpha_i + \beta_i + \gamma_i}\times \frac{ {p_i^{\max(\alpha_i,\beta_i,\gamma_i)}}} { {p_i^{\max(\alpha_i,\beta_i)+\max(\alpha_i,\gamma_i)+\max(\beta_i,\gamma_i)}}})}\\ &=\prod_{i=1}^{n}{ {p_i}^{\alpha_i + \beta_i + \gamma_i + \max(\alpha_i,\beta_i,\gamma_i) - \max(\alpha_i,\beta_i)-\max(\alpha_i,\gamma_i)-\max(\beta_i,\gamma_i)}}\\ &=\prod_{i=1}^{n}{ {p_i}^{\min{(\alpha_i,\beta_i,\gamma_i)}}}=gcd(a,b,c)\\ \end{align*} 证明: a左边a×b×c×lcm(a,b)×lcm(a,c)×lcm(b,c)lcm(a,b,c)=gcd(a,b,c).=i=1npiαi,b=i=1npiβi,c=i=1npiγi, 其中 pi 为质数。=i=1npiαi×i=1npiβi×i=1npiγi×i=1npimax(αi,βi)×i=1npimax(βi,γi)×i=1npimax(αi,γi)i=1npimax(αi,βi,γi)=i=1n(piαi+βi+γi×pimax(αi,βi)+max(αi,γi)+max(βi,γi)pimax(αi,βi,γi))=i=1npiαi+βi+γi+max(αi,βi,γi)max(αi,βi)max(αi,γi)max(βi,γi)=i=1npimin(αi,βi,γi)=gcd(a,b,c)

#include <bits/stdc++.h>
using namespace std;
// typedef long long ll;
#define int long long
const int N = 1e6 + 5, INF = 0x3f3f3f3f, MOD = 1E9 + 7;

int gcd(int a, int b) {
    
    
    return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b) {
    
    
    return a / gcd(a, b) * b;
}
int lcm(int a, int b, int c) {
    
    
    return lcm(lcm(a,b), c);
}
int s(int a, int b, int c) {
    
    
    // return lcm(a, b, c) / lcm(a, b) * a / lcm(a, c) * b * c / lcm(b, c);
    return gcd(gcd(a, b), c);
}
void solve1() {
    
    
    int n, mx = 0;
    cin >> n;
    vector<int> a(n), b(3);
    for (int i = 0; i < n; i++) cin >> a[i];
    sort(a.begin(), a.end());
    for (int i = 0; i < n; i++) 
        for (int j = i + 1; j < n; j++)
            for (int k = j + 1; k < n; k++) {
    
    
                int x = s(a[i], a[j], a[k]);
                if (x > mx) mx = x, b[0] = a[i], b[1] = a[j], b[2] = a[k];
            }
    for (int i = 0; i < 3; i++)
        cout << b[i] << " \n"[i == 2];
}
bool check(const vector<int>& b, const vector<int>& v) {
    
    
    if (v.size() < 3) return false;
    int d1 = gcd(gcd(b[0], b[1]),b[2]);
    int d2 = gcd(gcd(v[0], v[1]),v[2]);
    if(d1 != d2) return d1 < d2;
    for (int i = 0; i < 3; i++)
        if (b[i] != v[i]) return b[i] > v[i];
    return false;
}
void solve2() {
    
    
    int n;
    cin >> n;
    vector<int> a(n), b(3, -1);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    map<int, vector<int>> mp;
    for (auto u : a) {
    
    
        for (int i = 1; i <= u / i; i++) {
    
    
            if (u % i == 0) {
    
     
                mp[i].push_back(u);
                if (i != u / i) mp[u / i].push_back(u);
            }
        }
    }
    for (auto& [u, v] : mp) {
    
    
        sort(v.begin(), v.end());
        if (b[0] == -1 || check(b, v))
            for (int i = 0; i < 3; i++) b[i] = v[i];
    }
    for (int i = 0; i < 3; i++)
        cout << b[i] << " \n"[i == 2];
}
signed main() {
    
    
    int t = 1;  // cin>>t;
    while (t--) {
    
    
        // solve1(); // 30%
        solve2(); // ac
    }
    return 0;
}

F 数字接龙(15分)

小蓝最近迷上了一款名为《数字接龙》的迷宫游戏。游戏在一个大小为 N × N N\times N N×N 的格子棋盘上展开,其中每一个格子处都有着一个 0 ∼ K − 1 0\sim K-1 0K1 之间的整数。游戏规则如下:

  1. 从左上角 ( 0 , 0 ) (0,0) (0,0) 处出发,目标是到达右下角 ( N − 1 , N − 1 ) (N-1,N-1) (N1,N1) 处的格子。每一步可以选择沿着水平、竖直、对角线方向移动到下一个格子。
  2. 路径经过的棋盘格子,上面的数字需要按照 0 , 1 , 2 , . . . , K − 1 , 0 , 1 , 2 , . . . 0, 1, 2, ..., K-1, 0, 1, 2, ... 0,1,2,...,K1,0,1,2,... 的顺序组成序列。
  3. 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
  4. 路径中不可以出现交叉的线路。例如,之前如果从 ( 0 , 0 ) (0,0) (0,0) 移动到 ( 1 , 2 ) (1,2) (1,2) ,那么再从 ( 1 , 0 ) (1,0) (1,0) 移动到 ( 0 , 1 ) (0,1) (0,1) 就会形成交叉线路。

为了方便表示行进方向,对可以行进的八个方向进行了数字编号,如图 2 所示。因此,行进路径可以用一个包含 0 ∼ 7 0\sim 7 07 之间数字的字符串表示。

图 1 展示了一个迷宫示例,其对应的答案是:41255214。
在这里插入图片描述

【分析】 此题是一个比较裸的dfs题目,主要的变化在于路径中不可以出现交叉的线路,如果考虑 (x,y)–>(a,b), 需要看是否存在 (x,b)<–>(a,y),数据范围较小,使用标记 ch[x][y][a][b] 表示 是否存在 (a,y)–>(a,b) 的情况。

本题目前未找到任何做法(在不进行特判的情况下)进行有效剪枝通过 n=10,k=1 的数据。

题目数据为随机生成。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 11, INF = 0x3f3f3f3f, MOD = 1E9 + 7;
int n, m, k, d[][2] = {
    
    -1, 0, -1, 1, 0, 1, 1, 1, 1, 0, 1, -1, 0, -1, -1, -1};
int s[N][N];
bool st[N][N], ch[N][N][N][N], fg = 0;
string ans;

void dfs(int x, int y, string path) {
    
    
    if (x == n - 1 && y == n - 1 || fg) {
    
    
        if (path.size() == n * n - 1)
            cout << path << endl, fg = 1, exit(0);
        return;
    }
    st[x][y] = 1;
    for (int i = 0; i < 8; i++) {
    
    
        int a = x + d[i][0], b = y + d[i][1];
        if (a < 0 || a >= n || b < 0 || b >= n) continue;
        if (st[a][b] || s[a][b] != (s[x][y] + 1) % k) continue;
        if (i % 2 && (ch[x][b][a][y] || ch[a][y][x][b])) continue;
        ch[x][y][a][b] = 1;
        dfs(a, b, path + to_string(i));
        ch[x][y][a][b] = 0;
    }
    st[x][y] = 0;
}
void solve() {
    
    
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) cin >> s[i][j];
    dfs(0, 0, "");
    if (!fg) cout << "-1\n";
}
signed main(signed argc, char* argv[]) {
    
    
    int t = 1;  // cin>>t;
    while (t--) {
    
    
        solve();
    }
    return 0;
}

G 爬山(20分)

错题

H 拔河(20分)

小明是学校里的一名老师,他带的班级共有n名同学,每名同学有一个力量值 a i a_i ai。在闲暇之余,小明决定在班级里组织一场拔河比赛。为了保证比赛的双方实力尽可能相近,需要在这n名同学中挑选出两个队伍,队伍内的同学编号连续。具体地,可以表示为两个连续的子序列: a l 1 , a l 1 + 1 , . . . , a r 1 − 1 , a r 1 {a_{l_1}, a_{l_1+1}, ..., a_{r_1-1}, a_{r_1}} al1,al1+1,...,ar11,ar1 a l 2 , a l 2 + 1 , . . . , a r 2 − 1 , a r 2 {a_{l_2}, a_{l_2+1}, ..., a_{r_2-1}, a_{r_2}} al2,al2+1,...,ar21,ar2,其中满足 l 1 ≤ r 1 < l 2 ≤ r 2 l_1 \le r_1 < l_2 \le r_2 l1r1<l2r2。两个队伍的人数不必相同,但是需要让队伍内的同学们的力量值之和尽可能相近。

  • 对于20%的数据,保证 n ≤ 50 n\le 50 n50

  • 对于100%的数据,保证 n ≤ 1 0 3 , a i ≤ 1 0 9 n\le10^3, a_i\le 10^9 n103,ai109
    【分析】

  • 对于20%的数据,可以考虑枚举两个区间,利用前缀和计算区间和,复杂度 O ( n 4 ) O(n^4) O(n4)

  • 明显需要 ≤ O ( n 2 l o g ) \le O(n^2 log) O(n2log) 的复杂度,考虑枚举一个区间,二分查找和该区间和最匹配的数据。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 5, INF = 0x3f3f3f3f, MOD = 1E9 + 7;
ll n,a[N*N/2],s[N];

void solve1(){
    
    
    ll ans=1e12;
   for(int l1=1; l1<n; l1++)
     for(int r1=l1; r1<n; r1++)
       for(int l2=r1+1; l2<=n; l2++)
         for(int r2=l2; r2<=n; r2++)
            ans = min(ans, abs(s[r2] - s[l2-1] - (s[r1] - s[l1-1])) );
    cout<<ans<<endl;
}
void solve2(){
    
    
    ll ans=1e12;
    set<ll> st;st.insert(1e12), st.insert(-1e12);
    for(int l=1; l<=n; l++){
    
    
        for(int i=1; i<l; i++) st.insert( s[l-1] - s[i-1] ); // [i, l-1]
        for(int r=l; r<=n; r++){
    
    
            ll sum = s[r] - s[l-1];
            auto it = st.lower_bound(sum);
            ans = min(ans, *it - sum);
            --it;
            ans = min(ans, sum - *it);
        }
    }
    cout<<ans<<endl;
}
int main(){
    
    
    cin>>n;
    for(int i=1; i<=n; i++) {
    
    
        cin>>a[i];
        s[i] = s[i-1] +a[i];
    }
    solve2();
}