)
蓝桥杯C/C大学B组真题解析从暴力破解到最优解附完整代码参加蓝桥杯竞赛的同学们都知道算法优化是决定成绩高低的关键因素。面对同一道题目暴力解法可能只能拿到基础分而经过优化的算法却能轻松获得满分。本文将以蓝桥杯C/C大学B组真题为例详细讲解如何从最直接的暴力解法入手逐步分析优化思路最终给出最优解的实现方案。1. 暴力解法理解问题的第一步暴力解法虽然效率不高但却是理解题目本质的最佳途径。以小球反弹问题为例题目要求计算小球在矩形区域内反弹的总路程。最直观的做法就是模拟小球的运动过程#includebits/stdc.h using namespace std; int main() { int dx 15, dy 17; int x 343720, y 233333; double distance 0; int steps 1000000; // 设置一个足够大的步数 for(int i 0; i steps; i) { distance sqrt(dx*dx dy*dy); // 模拟碰撞检测和方向变化 // ... } printf(%.2lf\n, distance); return 0; }这种解法的问题显而易见计算量大难以在限定时间内完成精度控制困难边界条件处理复杂提示暴力解法虽然不高效但在竞赛初期可以帮助你快速验证思路的正确性建议至少写出暴力解法的伪代码。2. 数学优化寻找规律与公式通过观察题目我们可以发现小球反弹问题实际上是一个数学问题。利用最大公约数(GCD)和最小公倍数(LCM)的性质可以大幅简化计算#includebits/stdc.h using namespace std; int gcd(int a, int b) { return b 0 ? a : gcd(b, a % b); } int main() { int dx 15, dy 17; int x 343720, y 233333; int q dy * x, p dx * y; int g gcd(p, q); p / g, q / g; int t 2 * p * x / dx; double ans t * sqrt(dx*dx dy*dy); printf(%.2lf\n, ans); return 0; }优化后的算法时间复杂度从O(n)降低到O(1)计算结果精确无误代码简洁易于理解和维护3. 数据结构优化提升查询效率在宝石组合问题中我们需要找到三个数使其最大公约数最大。暴力三重循环的复杂度是O(n³)对于n1e5的数据显然不可行。通过使用哈希表存储每个数的所有因数我们可以将复杂度降低到O(n√n)#includebits/stdc.h using namespace std; typedef long long ll; int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); ll n; cin n; vectorll a(n); for(ll i 0; i n; i) cin a[i]; sort(a.begin(), a.end()); unordered_mapll, vectorll factor_map; for(ll num : a) { for(ll i 1; i sqrt(num); i) { if(num % i 0) { factor_map[i].push_back(num); if(num / i ! i) { factor_map[num/i].push_back(num); } } } } for(ll i a.back(); i 1; i--) { if(factor_map[i].size() 3) { cout factor_map[i][0] factor_map[i][1] factor_map[i][2]; return 0; } } cout a[0] a[1] a[2]; return 0; }这种优化思路的关键点预处理每个数的所有因数按从大到小的顺序检查可能的GCD值一旦找到符合条件的三个数立即返回结果4. 动态规划与记忆化搜索对于爬山问题我们需要在有限的操作次数内最小化山峰高度的总和。这个问题可以使用动态规划来解决#includebits/stdc.h using namespace std; typedef long long ll; int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); ll n, P, Q; cin n P Q; vectorll h(n); for(ll i 0; i n; i) cin h[i]; // dp[i][j][k] 表示前i座山用了j次P操作和k次Q操作的最小总和 vectorvectorvectorll dp(n1, vectorvectorll(P1, vectorll(Q1, LLONG_MAX))); dp[0][0][0] 0; for(int i 1; i n; i) { for(int j 0; j P; j) { for(int k 0; k Q; k) { ll min_val LLONG_MAX; // 不操作 if(dp[i-1][j][k] ! LLONG_MAX) { min_val min(min_val, dp[i-1][j][k] h[i-1]); } // 使用P操作 if(j 0 dp[i-1][j-1][k] ! LLONG_MAX) { ll new_h sqrt(h[i-1]); min_val min(min_val, dp[i-1][j-1][k] new_h); } // 使用Q操作 if(k 0 dp[i-1][j][k-1] ! LLONG_MAX) { ll new_h h[i-1] / 2; min_val min(min_val, dp[i-1][j][k-1] new_h); } dp[i][j][k] min_val; } } } cout dp[n][P][Q]; return 0; }动态规划的优势在于可以系统地考虑所有可能的操作组合避免重复计算提高效率保证找到全局最优解5. 深度优先搜索与剪枝策略数字接龙问题要求找到一条符合特定规则的路径这类问题通常使用DFS配合剪枝策略#includebits/stdc.h using namespace std; const int N20; int dx[]{-1,-1,0,1,1,1,0,-1}; int dy[]{0,1,1,1,0,-1,-1,-1}; int k,n; bool acfalse; vectorint now,res; int e[N][N]; bool st[N][N]; void dfs(int x,int y,int cnt) { if(ac) return; if(x n y n now.size() (n*n -1)) { ac true; res now; return; } int nxp cnt % k; for(int i0; i8; i) { int nxxdx[i], nyydy[i]; if(nx1||nxn||ny1||nyn||st[nx][ny]) continue; if(e[nx][ny]!nxp) continue; // 剪枝避免绕路 if(i1st[x-1][y]st[x][y1]) continue; if(i3st[x1][y]st[x][y1]) continue; if(i5st[x1][y]st[x][y-1]) continue; if(i7st[x-1][y]st[x][y-1]) continue; st[nx][ny]true; now.push_back(i); dfs(nx,ny,cnt1); now.pop_back(); st[nx][ny]false; } } int main() { cinnk; for(int i1;in;i) for(int j1;jn;j) cine[i][j]; st[1][1]true; dfs(1,1,1); if(res.empty()) cout-1endl; else { for(int dir:res) coutdir; coutendl; } return 0; }关键优化点方向数组预处理简化代码提前终止标志(ac)找到解后立即返回多种剪枝条件减少不必要的搜索使用标记数组避免重复访问6. 高精度计算技巧R格式问题涉及大数运算需要特别注意精度处理#includebits/stdc.h using namespace std; typedef long long ll; void mul(vectorll num) { ll carry 0; for(ll i 0; i num.size(); i) { num[i] num[i] * 2 carry; carry num[i] / 10; num[i] % 10; } if(carry) num.push_back(carry); } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); ll n; cin n; string d; cin d; bool is_negative (d[0] -); if(is_negative) d d.substr(1); reverse(d.begin(), d.end()); vectorll num; size_t dot_pos string::npos; for(size_t i 0; i d.size(); i) { if(d[i] .) { dot_pos i; continue; } num.push_back(d[i] - 0); } while(n--) mul(num); while(!num.empty() num.back() 0) num.pop_back(); if(dot_pos ! string::npos) { if(dot_pos 0 num[dot_pos-1] 5) { // 处理四舍五入 num[dot_pos] 1; for(size_t i dot_pos; i num.size(); i) { if(num[i] 10) { num[i] - 10; if(i1 num.size()) num[i1]; else num.push_back(1); } } } num.erase(num.begin(), num.begin() dot_pos); } reverse(num.begin(), num.end()); if(is_negative) cout -; for(ll digit : num) cout digit; return 0; }高精度计算的要点使用数组或vector按位存储数字注意进位处理特别处理小数点位置四舍五入要谨慎前导零和后导零的处理7. 综合应用拔河问题的最优解最后我们来看拔河问题它综合了前缀和、排序和区间处理等多种技巧#includebits/stdc.h using namespace std; typedef long long ll; int main() { ll n; cin n; vectorll a(n1, 0), s(n1, 0); for(ll i 1; i n; i) { cin a[i]; s[i] s[i-1] a[i]; } vectorpairll,ll intervals; for(ll i 1; i n; i) { for(ll j i; j n; j) { intervals.emplace_back(s[j]-s[i-1], i); } } sort(intervals.begin(), intervals.end()); ll min_diff LLONG_MAX; for(ll i 0; i intervals.size()-1; i) { ll l1 intervals[i].second; ll r1 l1 (intervals[i].first / a[l1]); ll l2 intervals[i1].second; ll r2 l2 (intervals[i1].first / a[l2]); if(r1 l2 || r2 l1) { min_diff min(min_diff, intervals[i1].first - intervals[i].first); } } cout min_diff endl; return 0; }优化思路预处理前缀和数组快速计算任意区间和生成所有可能的区间和及其起始位置排序后检查相邻区间是否重叠在不重叠的区间对中寻找最小差值在实际竞赛中我通常会先写出暴力解法确保理解题意然后分析问题特性寻找优化空间。比如发现区间和的重复计算就考虑前缀和遇到大数运算就准备高精度模板面对搜索问题则设计合理的剪枝策略。