news 2026/2/3 8:16:15

算法题目优选(蓝桥杯备战)--2

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题目优选(蓝桥杯备战)--2

文章目录

  • 前言
  • 分享
  • 题目清单
    • 1.奶牛晒衣服
    • 2.砝码称重
    • 3.螺旋矩阵
    • 4.“非常男女”计划
    • 5.次大值
    • 6.单词接龙
    • 7.瑞士轮
    • 8. 奶酪

前言

这些题目摘录于洛谷,好题,典型的题,考察各类算法运用,可用于蓝桥杯及各类算法比赛备战,算法题目练习,提高算法能力,补充知识,提升思维。
锻炼解题思路,从学会算法模板后,会分析,用到具体的题目上。

分享

试想一下多年以后,你真的成为自己最喜欢的样子,那么此刻如果身处低谷,你真的不再为自己努力尝试一下吗?每个人的起点不同,不要为此感到愤恨不公,把握有限的时间,让自己变的更好,用更扎实的学识拖住你的犹豫,用更坚毅的品质挡住你的迷茫。当一切实现,你也许会觉得努力不是独自的成长,也不是突然的蜕变,而是你面对理想的自己,日复一日的靠近。

题目清单

1.奶牛晒衣服

题目:P1843 奶牛晒衣服

解法:二分答案
首先大概率会想到贪心算法,就是优先选择湿度最大的衣服进行烘干,然后选择次之,但是这样的贪心策略是错误的,可以举出反例:

w = [10, 8], a = 1, b = 1; 如果选10, 算出来时间是7s, 但最优解是用烘干机烘干:10 选择烘干4s, 8烘干1s, 结果是6是。

为什么会错? 因为把自然干看成为每个都有烘干能力为a/s的烘干机,而这种贪心策略在最后浪费了其它烘干机在的烘干湿度。所以最好让最后衣服一起可以烘干。

可以发现烘干机在中途要不断改变烘干的衣服。 这种是很难直接找的。

枚举答案:

设经过的时间是 x 。根据题意,我们可以发现如下性质:

经过的时间如果是 x 的话,烘干机的「使⽤次数」最多也是 x ;

x 与能弄干的衣服在呈正相关;

那么在整个解空间里面,设弄干所有衣服的最少时间是 ret ,于是有:

当 x ≥ ret 时,我们能弄干所有衣服; 满足要求(合法)

当 x < ret 时,我们不能弄干所有衣服。在解空间中,根据ret 的位置,可以将解集分成两部分,具有「⼆段性」,那么我们就可以⼆分答案。不满足要求(不合法)

接下来的重点就是,给定⼀个时间 x,判断「是否能把所有的衣服全部弄干」。当时间为 x时,所

有衣服能够自然蒸发a * x的湿度,于是:

如果 w[i] ≤ a ⋅ x :让它自然蒸发;

如果 :需要⽤烘⼲机烘干的湿度,次数为

w[i] > a ⋅ x t = w[i] − a ⋅ x + t / b + (t % b == 0 ? 0 : 1) //优先级:算术 > 逻辑

那么我们可以遍历所有的衣服,计算烘干机的「使用次数」与 x进行判断。

如果使用次数「大于」给定的时间 x ,说明不能全部弄干;

如果使用次数「小于等于」给定的时间 x ,说明能全部弄干。

注意:虽然这里int也能全部通过,这里数据范围是到 5e5 ,但是拿不准还是开long long。

代码:

#include<iostream>usingnamespacestd;constintN=5e5+10;typedeflonglongLL;LL n,a,b;LL w[N];//判断boolcheck(LL x){intcnt=0;for(inti=1;i<=n;i++){if(w[i]<=a*x)continue;intd=w[i]-a*x;cnt+=d/b+(d%b==0?0:1);}returncnt<=x;}intmain(){cin>>n>>a>>b;for(inti=1;i<=n;i++)cin>>w[i];//二分答案LL l=1,r=5e5;while(l<r){LL mid=(l+r)/2;if(check(mid))r=mid;elsel=mid+1;}cout<<l<<endl;return0;}

2.砝码称重

题目:P2347 [NOIP 1996 提高组] 砝码称重

解法:多重背包
求得是可以凑出得重量种数,首先想到暴力枚举,排列组合等方法,但难以找到枚举或排列的方法,且时间复杂度大,过于复杂。

重量: w:[1, 2, 3, 5, 10, 20]

数量: a[] :[a1, a2, a3, a4, a5, a6]

思路转化:设选的 w总 = j,这道题就转化为从前i种物品中选物品,总重量为j的所有选法。这样题目就转化为多重背包问题(物品个数有限制)。

执行多重背包的逻辑:
1.状态表式:
表示:从前 i 种砝码中挑选,是否能凑成总质量为 j 。
最终结果就是 f 表里面最后⼀⾏为 true 的个数。f[n] [j]里面找, f[n] [0] 不算。

2.状态转移⽅程:
根据第 i 个砝码选的个数,能分成 a[i] + 1 种情况,设选了0,1, …… k 个。
此时重量为 w[i] × k ,那么就应该去前⾯看看能不能凑成 j − w[i] × k ,即:f[i - 1] [j - w[i] * k]
在所有的情况里面,只要有⼀个为真, f[i] [j] 就为真。

3.初始化:
f[0] [0] = 1,起始状态,也是为了后续填表有意义且正确。

4.填表顺序:
从上往下每一行,每一行从左往右。

数据范围, 可知 j <= 1000,用于循环更新所有,选的情况。

细节:优化一维,第二层for要逆序处理, 0 <= k <= a[i], k * w[i] <= j.

代码:

#include<iostream>usingnamespacestd;constintN=1010;intw[]={0,1,2,3,5,10,20};inta[10];intf[N];//f[i][j]从前i个物品中挑选,能否凑出重量为jintmain(){for(inti=1;i<=6;i++)cin>>a[i];//初始化f[0]=1;//多重背包for(inti=1;i<=6;i++){for(intj=1000;j>=0;j--)//优化一维,逆序处理{for(intk=0;k<=a[i]&&k*w[i]<=j;k++){f[j]=f[j]||f[j-k*w[i]];//有一个成立即可}}}intret=0;for(inti=1;i<=1000;i++){if(f[i])ret++;}cout<<"Total="<<ret<<endl;return0;}

3.螺旋矩阵

题目:P2239 [NOIP 2014 普及组] 螺旋矩阵

解法:找规律 + 分情况讨论 + 递归
1.暴力模拟:直接按顺序填数,O(n ^ 2)时间复杂度还在范围内,但是空间超了。

2.发现:会出现在边缘不在边缘两种情况;那么可以一层一层的分析,在边缘(属于边界条件,可以直接数学计算),当 (i, j) 的位置不在边缘的时候,接下来去:将主问题(边长为 n 的矩形,从 0 开始累加,找出 (i, j) 位置的值;)分割为子问题(边长为 n - 2 的矩形,从 4 * (n - 1) 开始累加,找出 (i -1, j - 1) 位置的值。) 注意:此时要查找的位置是i - 1, j - 1相同子问题,可以用递归来解决。

3.边界处理:

细节: 每一圈的开始位置不同,用一个begin标记每一圈的开始位置

i=1, begin + j;

j = n, begin + i + n - 1;

i = n, begin + 2 * (n - 1) + (n - j) + 1;

j = 1, begin + 3 * (n - 1) + (n - i) + 1。

代码:

#include<iostream>usingnamespacestd;intn,i,j;//递归intdfs(intn,intbegin,inti,intj){//边界,递归出口if(i==1)returnbegin+j;if(j==n)returnbegin+i+n-1;if(i==n)returnbegin+3*n-j-1;if(j==1)returnbegin+4*n-i-2;//下一层returndfs(n-2,begin+4*(n-1),i-1,j-1);}intmain(){cin>>n>>i>>j;cout<<dfs(n,0,i,j)<<endl;return0;}

4.“非常男女”计划

题目:P1114 “非常男女”计划

解法:正负抵消 + 前缀和 + 哈希表
找一个区间内男女个数相等,1,0; sum * 2 = len; 但是这样判断很麻烦。

提供思路:要确定一个区间的两种东西数量是否相等,可以用1,-1表示,看区间和是否为0,正负抵消。

再学会用前缀和(思路) + 哈希表 维护区间的值。 思维转化:要找一个区间和为0的最长区间, 区间为sum,那么用前缀和维护区间和,利用哈希表找第一个出现sum的位置,两者区间的差部分的那个区间为0区间,且满足最长,就找第一个,用mp.count(sum)

细节:要初始化mp[0] = 0, 因为本身区间可能sum = 0,但是之前没有0, 就算不到这种情况区间的长度。

代码:

#include<iostream>#include<unordered_map>usingnamespacestd;intn;unordered_map<int,int>mp;// 1.前缀和sum 2.对应下标intmain(){cin>>n;intsum=0,ret=0;mp[0]=0;//注意要初始化,sum = 0, i = 0for(inti=1;i<=n;i++){intx;cin>>x;x=(x==0?-1:1);sum+=x;if(mp.count(sum))ret=max(ret,i-mp[sum]);//count,第一次出现sum的值(位置),区间长度elsemp[sum]=i;//第一次出现sum}cout<<ret<<endl;return0;}

5.次大值

题目:P5682 [CSP-J 2019 江西] 次大值

解法:数学
严格次大值,可以对所有的数据排序 + 去重。细节:虽然排序后去重,取模后也会有重复的数,但是这个算法原理不考虑。

排序数组a:最大值分为两种情况:因为大数 % 小数 < 小数, < a[n - 1], 小数 % 大数 max = a[n - 1],最大值⼀定是 a[n - 1] % a[n]。

同理,次大值可以分为两种情况:

1.小数 % 大数:那就只能是 a[n - 2] % a[n - 1] ; a[n - 2], < a[n - 2]不考虑

2.大数 % 小数:只能是 a[n] % a[n - 1] 。 < a[n - 1]

在这两种情况中取最大值即可。

代码:

#include<iostream>#include<algorithm>usingnamespacestd;constintN=2e5+10;intn;inta[N];intmain(){cin>>n;for(inti=1;i<=n;i++)cin>>a[i];sort(a+1,a+1+n);n=unique(a+1,a+1+n)-(a+1);if(n==1)cout<<-1<<endl;elseif(n==2)cout<<a[2]%a[1]<<endl;elsecout<<max(a[n-2],a[n]%a[n-1])<<endl;return0;}

6.单词接龙

题目:P1019 [NOIP 2000 提高组] 单词接龙

解法:dfs暴搜 + 剪枝
因为这道题的数据范围很小, n <= 20, dfs时间复杂度是呈指数级别的。

大致思路:从开头开始,暴力搜索出所有的连接情况,加入大量判断与更新,剪枝,找出最长的。

这道题有大量的细节问题要处理:

用字符串数组s[i]存储所有字符串,进行暴力搜索(dfs)。

1.不能用常规的做法用全局的path标记当前路径,因为不能“清理(恢复)现场”就是恢复回到上一步(递归回溯),会找不到上一条路的情况;因为以往做的都是操作简单,可以+ - 一个数或字符(pop_back() , push_back() )回到上一步,但是这里涉及到拼接就会改变很多,很乱,处理起来很复杂。 这里设计dfs(string path) 参数对应,就保留了每一步的路径(字符串的连接情况)。

2.如何拼接:用双指针和substr拼接字符串函数,判断path.substr(cur1) == s[i].substr(0, cur2 + 1) substr()传1个参数是用这个及后面的字符,传两个参数是开始和切的长度。 然后双指针同时移动:cur1–, cur++, 拼接:path + s[i].substr(cur2 + 1)。

3.处理不能包含的问题:给指针限定移动范围:cur1 >= 1, cur2 < s[i].size() - 1。

4.因为处理了不能包含的情况,但是单词接龙的龙头是包含的情况,要特殊处理,就用一个for循环遍历字符串数组,s[i] [0] = ch; (这里可以看成一个二维数组遍历) dfs(s[i])。

代码:

#include<iostream>usingnamespacestd;constintN=30;intret,n;string s[N];intcnt[N];//同bool的作用,标记字符串用了几次,用于剪枝(非法)voiddfs(string path){if(path.size()>ret)ret=path.size();//记得更新长度for(inti=1;i<=n;i++){if(cnt[i]>=2)continue;//剪枝intcur1=path.size()-1,cur2=0;while(cur1>=1&&cur2<path.size()-1){if(path.substr(cur1)==s[i].substr(0,cur2+1))//长度为 cur2 + 1{cnt[i]++;dfs(path+s[i].substr(cur2+1));//拼接cnt[i]--;//恢复现场,回溯}cur1--;cur2++;}}}intmain(){cin>>n;for(inti=1;i<=n;i++)cin>>s[i];charch;cin>>ch;for(inti=1;i<=n;i++){if(s[i][0]==ch){cnt[i]++;dfs(s[i]);cnt[i]--;//恢复现场,回溯}}cout<<ret<<endl;return0;}

7.瑞士轮

题目:P1309 [NOIP 2011 普及组] 瑞士轮

解法:模拟 + 归并排序
直接模拟时间复杂度会超。

创建两个数组,胜者组和败者组。要降序排序,因为要输出分数最大的人的id,因为每个人有id,实力值,分数3个数据,排序时要对应,用struct存储。 O(n * r + nlogm)

每一轮:

1.先按分数排序;(降序

2.模拟一轮比赛过程,比赛结果放入胜者组和败者组。 (降序

3.用归并排序的逻辑合并有序数组

代码:

#include<iostream>#include<algorithm>usingnamespacestd;constintN=2e5+10;//开2倍空间intn,r,q;structnode{ints,id,w;}a[N];node x[N],y[N];//胜者组和败者组//升序boolcmp(node&x,node&y){if(x.s==y.s)returnx.id<y.id;elsereturnx.s>y.s;}//归并排序合并有序数组逻辑voidmerge(){intcur1=1,cur2=1,i=1;while(cur1<=n&&cur2<=n){if(x[cur1].s>y[cur2].s||(x[cur1].s==y[cur2].s&&x[cur1].id<y[cur2].id)){a[i++]=x[cur1++];}else{a[i++]=y[cur2++];}}//其中必有一个剩余,处理剩下的while(cur1<=n)a[i++]=x[cur1++];while(cur2<=n)a[i++]=y[cur2++];}intmain(){cin>>n>>r>>q;for(inti=1;i<=n+n;i++){cin>>a[i].s;a[i].id=i;}for(inti=1;i<=n+n;i++){cin>>a[i].w;}sort(a+1,a+1+n+n,cmp);while(r--){intpos=1;for(inti=1;i<=n+n;i+=2)//一次两个比较,注意这里 i += 2{if(a[i].w>a[i+1].w){a[i].s++;x[pos]=a[i];y[pos]=a[i+1];}else{a[i+1].s++;x[pos]=a[i+1];y[pos]=a[i];}pos++;}merge();}cout<<a[q].id<<endl;return0;}

8. 奶酪


题目:P3958 [NOIP 2017 提高组] 奶酪

解法:并查集(妙解)
判断是否联通,联通的话放入一个集合,在最上表面n+1和最下表面0各增加一块,最后判断这两块是否连通

代码:

#include<iostream>usingnamespacestd;typedeflonglongLL;constintN=1e3+10;LL n,h,r;LL x[N],y[N],z[N];intfa[N];//并查集intfind(intx){returnfa[x]==x?x:fa[x]=find(fa[x]);}voidmerge(intx,inty){fa[find(x)]=find(y);}boolcheck(inti,intj){LL d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]);returnd<=4*r*r;}intmain(){intT;cin>>T;while(T--){cin>>n>>h>>r;//初始化for(inti=0;i<=n+1;i++)fa[i]=i;for(inti=1;i<=n;i++){cin>>x[i]>>y[i]>>z[i];//判断下表面if(z[i]<=r)merge(i,0);//判断上表面if(z[i]+r>=h)merge(i,n+1);//判断其余的球for(intj=1;j<i;j++){if(check(i,j))merge(i,j);}}if(find(0)==find(n+1))cout<<"Yes"<<endl;elsecout<<"No"<<endl;}return0;}

让我们保持好奇,保持耐心,在纷繁的数据结构中寻找秩序,在复杂的逻辑中构建桥梁。共勉!”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/28 20:30:26

英雄联盟游戏助手:让你的排位赛效率翻倍的秘密武器

还在为繁琐的游戏操作而烦恼吗&#xff1f;想要在英雄联盟中快速提升段位却总是卡在操作细节上&#xff1f;今天为你介绍一款能够真正改变游戏体验的智能助手&#xff0c;它将成为你冲击更高段位的最佳搭档。 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工…

作者头像 李华
网站建设 2026/1/28 10:46:07

SuperCom串口调试终极指南:从新手到专家的快速精通教程

SuperCom串口调试终极指南&#xff1a;从新手到专家的快速精通教程 【免费下载链接】SuperCom SuperCom 是一款串口调试工具 项目地址: https://gitcode.com/gh_mirrors/su/SuperCom SuperCom串口调试工具是嵌入式开发和硬件通信领域的专业利器&#xff0c;支持多串口监…

作者头像 李华
网站建设 2026/1/30 23:08:03

科学文库CAJ文档处理方案:提升知识管理效率的工具

科学文库CAJ文档处理方案&#xff1a;提升知识管理效率的工具 【免费下载链接】ScienceDecrypting 项目地址: https://gitcode.com/gh_mirrors/sc/ScienceDecrypting 还在为科学文库下载的文档只能在特定时间内使用而烦恼吗&#xff1f;是否曾经遇到过重要的学术论文突…

作者头像 李华
网站建设 2026/1/30 17:28:13

附件-–-behaviac

原文 附件&#xff08;Attachments&#xff09;类型可以附加到相应的节点上&#xff0c;在编辑器中有两类附件&#xff1a;显式附件和隐式附件。 1 显式附件 显式附件包括前置和后置&#xff0c;如下图所示&#xff1a; 可以在任何一个节点通过鼠标拖拽来添加前置或后置附件…

作者头像 李华
网站建设 2026/2/2 4:47:11

Windows 7系统下Umi-OCR兼容方案:让老旧设备也能高效文字识别

Windows 7系统下Umi-OCR兼容方案&#xff1a;让老旧设备也能高效文字识别 【免费下载链接】Umi-OCR Umi-OCR: 这是一个免费、开源、可批量处理的离线OCR软件&#xff0c;适用于Windows系统&#xff0c;支持截图OCR、批量OCR、二维码识别等功能。 项目地址: https://gitcode.c…

作者头像 李华
网站建设 2026/1/28 0:22:59

联想拯救者工具箱完整指南:解锁硬件潜能的一站式解决方案

联想拯救者工具箱完整指南&#xff1a;解锁硬件潜能的一站式解决方案 【免费下载链接】LenovoLegionToolkit Lightweight Lenovo Vantage and Hotkeys replacement for Lenovo Legion laptops. 项目地址: https://gitcode.com/gh_mirrors/le/LenovoLegionToolkit 联想拯…

作者头像 李华