前言:
本人不是ACMer!!!(大佬可以跳过了😅)去年用Python打过🏀杯,后来发现大多数高校都是C++,有些不支持Python语言,而且过去一年一直泡在实验室肝科研没咋练过题,所以改用C++把之前总结的模板和算法再拾一拾。写博客一方面敦促自己每天刷题,另一方面给和我一样算法蒟蒻cs保研er提供参考,加油吧陌生人!
所用网站: 洛谷 + Leetcode(力扣) 以洛谷为主是为了保证全流程规范刷题(尤其输入输出,力扣没有这些),力扣为辅是为了多见见题型。
ps:文章只提供题目链接和我自己写的题解(结合网上的优解),偶尔典型题会加一下自己的见解(这些题网上教程讲解已经很详细了:“前人之述备矣”,而且都2025年了不会还有人不会用大模型辅助刷题吧)
另外下面这个专栏里有我去年打比赛整理的经典算法模板(全网综合提炼加上自己的优化修改,精华中的精华),虽然是用python写的但算法模板放在哪个语言上都大差不差,可以去看一下(别忘了点赞收藏哦♥)后续模板会接着更新
一、入门基础题
这里主要是一些练手题俗称签到题,熟悉C++基本语法
#include <iostream>
using namespace std;
int main(){
int a,b;
cin >> a >>b;
cout << a+b;
return 0 ;
}
#include <iostream>
#include <string>
using namespace std;
int main(){
string s;
getline(cin,s); //注意getline函数的使用
int res = 0;
for(int i = 0;i<s.size();i++){
if (s[i] != ' ' &&s[i] != '\n'){
res ++;
}
}
cout << res;
return 0;
}
这道题一个注意遇到分数要定义double类型,还有最后输出边界的考量(n还是n-1)
#include<iostream>
using namespace std;
int main(){
int k,n;
double s,ans;
cin >> k;
s =0;
n = 1;
while(s<=k){
s += 1.0/n;
n += 1;
}
cout << n-1 << endl;
return 0;
}
这道题稍微上点难度,有两种解法从不同的角度出发,都写了详细注释
// 第一种解法:
#include <iostream>
#include <string>
using namespace std;
bool st[10]; //标记数组,st[i] 表示数字 i(1–9)是否已经被使用过。
string str; //用来动态拼接当前已选的数字字符,总长度最多 9(对应 9 个数字)。
void dfs(int x){ //参数 x 表示当前已经放了多少个数字字符(初始为 0)。
// 当已经选满 9 个数字时,尝试检验比例
//stoi: 将数字字符转化位int输出
if(x == 9){
int a = stoi(str.substr(0,3)) ;//substr:获得str中从第0位到长度为3的字符串
int b = stoi(str.substr(3,3));
int c = stoi(str.substr(6,3));
if(a*2 == b && a*3 == c){
cout <<a<<" "<<b<<" "<<c<<endl;
}
return;// 回到上一层,继续枚举其它排列
}
// 枚举下一个位置要放的数字
for(int i = 1;i<10;i++){
if(!st[i]){ // 如果数字 i 还没被用过
st[i]=true; // 标记为已用
str.push_back(i+'0'); // 把 i 转成字符,追加到 str 末尾
dfs(x+1);// 递归处理下一个位置
st[i] = false; // 回溯:撤销使用标记
str.pop_back(); // 回溯:删除刚才追加的字符
}
}
// 深度优先枚举的思路,就是把 1…9 全部排列(共 9! 种),在排列的基础上取前三位、次三位、末三位分别当作 a,b,c,再筛选比例。
}
int main(){
dfs(0);
return 0;
}
----------------------
//另一种方法:
#include <iostream>
using namespace std;
// 检查 n 的三个十进制数字,
// 在标记数组 vis 上打标记;
// 如果发现 0 或重复数字,返回 false。
bool mark3(int n, bool vis[]) {
for (int i = 0; i < 3; i++) {
int d = n % 10;
if (d == 0 || vis[d]) return false;
vis[d] = true;
n /= 10;
}
return true;
}
int main() {
// 枚举 a,从 123 开始到 329(因为 3*330 = 990 > 999)
for (int a = 123; a <= 329; a++) {
int b = 2 * a;
int c = 3 * a;
if (b >= 1000 || c >= 1000) continue;
bool vis[10] = {}; // vis[1..9] 用来标记数字是否出现过
// 先标记 a 的三位
if (!mark3(a, vis)) continue;
// 再标记 b 的三位
if (!mark3(b, vis)) continue;
// 再标记 c 的三位
if (!mark3(c, vis)) continue;
// 最后检查 1..9 都被标记过
bool ok = true;
for (int d = 1; d <= 9; d++) {
if (!vis[d]) { ok = false; break; }
}
if (ok) {
cout << a << " " << b << " " << c << "\n";
}
}
return 0;
}
这里有两种方法,这里补充一下ceil这一类cmath标准库: C++取整
//笨方法
#include<iostream>
using namespace std;
int main(){
int n,a1,a2,b1,b2,c1,c2,res1,res2,res3,ans;
// n 为铅笔数量 a,b,c为三种包装(下标1为数量,2为价格) res为每种包装的总价,ans为最终价格
cin >>n>>a1>>a2>>b1>>b2>>c1>>c2;
if(n % a1 != 0) res1 = (n/a1 +1)*a2 ;//重点理解这里取整和取余
else res1= (n/a1)*a2;
if(n % b1 != 0) res2 = (n/b1 +1)*b2 ;
else res2= (n/b1)*b2;
if(n % c1 != 0) res3 = (n/c1 +1)*c2 ;
else res3= (n/c1)*c2;
//比较
if(res1<res2)ans = res1;
else ans = res2; //注意这里比较后赋值可减少比较次数
if(res3<ans) ans = res3;
cout <<ans ;
return 0;
}
//使用内置函数快捷
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int main(){
int n,a1,a2,b1,b2,c1,c2,res1,res2,res3,ans;
// n 为铅笔数量 a,b,c为三种包装(下标1为数量,2为价格) res为每种包装的总价,ans为最终价格
cin >>n>>a1>>a2>>b1>>b2>>c1>>c2;
res1 = ceil(1.0*n/a1)*a2; //乘1.0是为了确保 ceil内为浮点数而非整数
res2 = ceil(1.0*n/b1)*b2;
res3 = ceil(1.0*n/c1)*c2;
ans = min({res1,res2,res3});
cout <<ans ;
return 0;
}
4.15记录-----未完待续......
取整/取余是常见的操作数字位数的手段
//因为反转后0要舍去,故不宜转为字符串
#include<bits/stdc++.h>
using namespace std;
int main(){
int n = 0,s = 0;
for(cin>>n; n!=0; n/=10) s = s*10+n%10;
cout << s;
return 0 ;
}
(7)P1014 [NOIP 1999 普及组] Cantor 表
很考验思维的一道题,建议多思考思考,对锻炼模拟和枚举思维很有帮助
//模拟枚举
#include<bits/stdc++.h>
using namespace std;
int main(){
//z编号后每一行比上一行多一
int n , k = 1;
cin >> n;
//每次k+1,判断最终的位置,k的奇偶判断正(如2)反(如3)
//出循环后此时k表示第k条对角线,n表示在该对角线上的位置。
while(n>k){
n = n-k;
k++;
}
//k偶数行分子随第几(n)顺序递增,也就是分子为n,分母从k行开始递减(k,k-1,k-2,k-3...)第n个项的分母为k-(n-1)=k+1-n(观察规律可得) k为奇则反过来
if (k%2 ==0){
cout << n << "/"<< (k+1-n);
}
else cout << (k+1-n) << "/" << n;
return 0;
}
----------4.19未完待续......
二、数组基础题
(8)P1046 [NOIP 2005 普及组] 陶陶摘苹果
基础数组题
#include<bits/stdc++.h>
using namespace std;
int main(){
int num[10],n,res=0;
for(int i = 0;i<10;i++) cin >> num[i];
cin >> n;
for(int i =0;i<10;i++){
if(n+30 >= num[i]) res++;
}
cout << res;
return 0;
}
数组操作题,easy
#include<bits/stdc++.h>
using namespace std;
int m[101],c=0;
int main(){
for(int i=0;;i++){
cin>>m[i];
if(m[i]==0) break;
c = i;
}
for(int j=c;j>=0;j--)
cout << m[j] <<' ';
return 0 ;
}
(10)P1047 [NOIP 2005 普及组] 校门外的树
标记数组
// 碰到重合问题 会用flag = true标记
#include<bits/stdc++.h>
using namespace std;
int res = 0;
bool num[10001]; //标记数组
int main(){
int n,m,l,r;
cin >> n>>m;
for (int i =0;i<m;i++){
cin >> l>>r;
for(int j = l;j<=r;j++){
num[j] = true ;//树已被记录
}
}
for (int i = 0;i<=n;i++){
if (!num[i]){
res++;
}
}
cout << res;
return 0;
}
三、字符串基础题
(11)P1055 [NOIP 2008 普及组] ISBN 号码
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,c;
char m,n;
scanf("%d-%d-%d-%c",&a,&b,&c,&m);
int tmp = (a+ (b / 100) * 2 + (b % 100 / 10) * 3 + (b % 10) * 4 + (c / 10000) * 5 + (c % 10000 / 1000) * 6 + (c % 1000 / 100) * 7 + (c % 100 / 10) * 8 + (c % 10) * 9)%11 ;
if(tmp==10) n = 'X';
else n = tmp+'0'; //+'0'转化为char
if (m==n) cout << "Right";//字符串必须使用双引号
else cout << a<<'-'<<b<<'-'<<c<<'-'<<n;
return 0;
}
(12)P5015 [NOIP 2018 普及组] 标题统计
签到题
#include<bits/stdc++.h>
using namespace std;
int main(){
int res = 0;
string s;
getline(cin,s);
for(int i=0;i<s.size();i++){
if(s[i]!=' '&& s[i]!= '\n') res ++;
}
cout << res;
return 0;
}
(13)P1308 [NOIP 2011 普及组] 统计单词数
#include<bits/stdc++.h>
using namespace std;
int main(){
string w,s;
// 输入目标单词并转换为小写
cin >> w ;
for(int i=0;i<w.size();i++) w[i] = tolower(w[i]);
//文章处理
getchar(); // 清除输入缓冲区中的换行符
getline(cin,s);
int id= -1; // 记录第一次出现的位置,初始值为-1表示未找到
int cut = 0;// 统计出现次数
//遍历
for(int i=0;i<s.size();i++){
if(s[i]== ' ')continue; //空格跳出本次循环
int st = i; //st为当前单词起始的位置
int e = 0; //用于确定单词的结束位置
e = i;
//将当前单词变小写,找到单词结束的位置
while(e<s.size()&&s[e]!=' '){
s[e]=tolower(s[e]) ;e++;
}
//进行比较
if(w == s.substr(st,e-st)) //substr:获得str中从第0位到长度
{
if(id == -1){
id =st; // 记录第一次出现的位置
}
cut++;
}
i= e-1;//更新主循环的索引
}
//输出
if(id==-1){ //由id可发现没找到
cout << id <<endl;
}else{
cout << cut<<' '<< id<<endl;
}
return 0;
}
(14)P2010 [NOIP 2016 普及组] 回文日期
#include<bits/stdc++.h>
using namespace std;
bool cmp(string a,string b){
return a+b > b+a;
}
int main(){
int n;
cin >> n;
string m[21];
for (int i = 0; i < n; i++) {
cin >> m[i];
}
//nums 数组从第一个元素到第 n 个元素(不包括 nums + n)按照自定义的比较函数 cmp 进行排序
sort(m,m+n,cmp);
for(int i=0;i<n;i++){
cout << m[i];
}
return 0;
}
--------4.28 记
以上是所有比较典型的基础入门题(不涉及复杂算法如递归、二分、模拟等)
下一篇将涉及递归和递推、简单dp、排序、模拟、分治这些简单算法,尽情期待~