算法笔记上机训练实战指南刷题记录
文章目录
- 算法笔记上机训练实战指南刷题记录
-
- 简单模拟
-
- [B1001 害死人不偿命的(3n+1)猜想](#B1001 害死人不偿命的(3n+1)猜想)
- [B1011 A+B 和 C](#B1011 A+B 和 C)
- [B1016 部分A+B](#B1016 部分A+B)
- [B1026 程序运行时间](#B1026 程序运行时间)
- B1046划拳
- B1008数组元素循环右移问题
- [B1012 数字分类](#B1012 数字分类)
- [B1018 锤子剪刀布](#B1018 锤子剪刀布)
- [B1046 Shortest Distance](#B1046 Shortest Distance)
- [1065 A+B and C (64bit)](#1065 A+B and C (64bit))
- [B1010 一元多项式求导](#B1010 一元多项式求导)
- [B1031 查验身份证](#B1031 查验身份证)
- [A1002 A+B for Polynomials](#A1002 A+B for Polynomials)
- [B1009 Product of Polynomials](#B1009 Product of Polynomials)
- 查找元素
-
- [B1041 考试座位号](#B1041 考试座位号)
- [B1004 成绩排名](#B1004 成绩排名)
- [B1028 人口普查](#B1028 人口普查)
- 每天两题,持续更新中~
简单模拟

题号 | 题目 | 分数 |
---|---|---|
B1001 | 害死人不偿命的(3n+1)猜想 | 15✔️ |
B1011 | A+B 和 C | 15✔️ |
B1016 | 部分A+B | 15✔️ |
B1026 | 程序运行时间 | 15✔️ |
B1046 | 划拳 | 15✔️ |
B1008 | 数组循环右移问题 | 20✔️ |
B1012 | 数字分类 | 20✔️ |
B1018 | 锤子剪刀布 | 20✔️ |
A1042 | Shuffling Machine | 20✔️ |
A1046 | Shortest Distance | 20✔️ |
B1065 | 1065 A+B and C (64bit) | 20 ✔️ |
B1010 | 一元多项式求导 | 20✔️ |
A1002 | A+B for Polynomials | 25✔️ |
A1009 | Product of Polynomials | 25✔️ |
B1031 | 查验身份证 | 15✔️ |
B1001 害死人不偿命的(3n+1)猜想
c++
#include <iostream>
using namespace std;
int n, cnt;
int main()
{
cin >> n;
while(n != 1)
{
if(n % 2)
n = (3 * n + 1) / 2;
else
n /= 2;
cnt ++;
}
cout << cnt << endl;
}
B1011 A+B 和 C
⚠️ A+B可能爆INT。需要开long long。

c++
#include <iostream>
using namespace std;
int n;
long long a, b, c;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a >> b >> c;
cout << "Case #" << i << ": " << (a + b > c ? "true" : "false") << endl;
}
return 0;
}
B1016 部分A+B
思路1:
枚举DA,DB出现的次数cntDA, cntDB, 计算PA, PB, 输出PA+PB
A,B用字符串存取,枚举DA,DB次数可以遍历A,B字符串。
c++
#include <iostream>
#include <cstring>
using namespace std;
string A, DA, B, DB;
int cntDA, cntDB;
long long resa, resb;
int main()
{
cin >> A >> DA >> B >> DB;
for(int i = 0; i < A.size(); i ++)
cntDA += (A[i] == DA[0]);
for(int i = 0; i < B.size(); i ++)
cntDB += (B[i] == DB[0]);
if(cntDA) resa = DA[0] - '0';
if(cntDB) resb = DB[0] - '0';
for(int i = 1; i < cntDA; i ++) resa = resa * 10 + (DA[0] - '0');
for(int i = 1; i < cntDB; i ++) resb = resb * 10 + (DB[0] - '0');
cout << resa + resb << endl;
return 0;
}
思路2:


c++
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
LL PA, PB, A, DA, B, DB;;
int main()
{
cin >> A >> DA >> B >> DB;
while(A)
{
int x = A % 10;
if(x == DA) PA = PA * 10 + DA;
A /= 10;
}
while(B)
{
int x = B % 10;
if(x == DB) PB = PB * 10 + DB;
B /= 10;
}
cout << PA + PB << endl;
return 0;
}
个人推荐思路2,纯粹的模拟做法。
B1026 程序运行时间

#include <iostream>
using namespace std;
int c1, c2, t;
int main()
{
cin >> c1 >> c2;
// t = (c2 - c1 + 50) / 100;
t = c2 - c1;
if(t % 100 >= 50) t = t / 100 + 1;
else t = t / 100;
printf("%02d:%02d:%02d\n", t / 3600, t % 3600 / 60, t % 60);
}
B1046划拳
failA,failB记录各自输的次数
c++
#include <iostream>
using namespace std;
int failA, failB;
int n;
int main()
{
cin >> n;
while(n --)
{
int a1, a2, b1, b2;
cin >> a1 >> a2 >> b1 >> b2;
if(a1 + b1 == a2 && a1 + b1 != b2) failB ++;
if(a1 + b1 == b2 && a1 + b1 != a2) failA ++;
}
cout << failA << " " << failB << endl;
return 0;
}
B1008数组元素循环右移问题
⚠️题目没有给定M的最大值,不能认为M<N。读入后需要令M=M%N。
直接输出N-M到N-1号元素,再输出0---N - M - 1号元素。
c++
#include <iostream>
using namespace std;
const int N = 110;
int a[N];
int n, m;
int main()
{
scanf("%d %d", &n, &m);
m %= n;
for(int i = 0; i < n; i ++)
scanf("%d", &a[i]);
for(int i = n - m; i < n; i ++)
cout << a[i] << " ";
for(int i = 0; i < n - m; i ++)
cout << a[i] << " \n"[ i == n - m - 1];
return 0;
}
B1012 数字分类
#include <iostream>
using namespace std;
const int N = 1010;
int n;
int A1, A2, A3, A4, A5;
int cntA2, cntA4;
int a[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
if(a[i] % 10 == 0) A1 += a[i];
if(a[i] % 5 == 1)
{
cntA2 ++;
if(cntA2 % 2 == 1) A2 += a[i];
else A2 += -1 * a[i];
}
if(a[i] % 5 == 2) A3 ++;
if(a[i] % 5 == 3) A4 += a[i], cntA4 ++;
if(a[i] % 5 == 4) A5 = max(A5, a[i]);
}
double a4 = A4 * 1.0 / cntA4;
printf("%d %d %d %.1lf %d", A1, A2, A3, a4, A5);
printf("%d %d %d %.1lf %d", A1, A2, A3, a4, A5);
printf("%d %d %d %.1lf %d", A1, A2, A3, a4, A5);
printf("%d %d %d %.1lf %d", A1, A2, A3, a4, A5);
printf("%d %d %d %.1lf %d", A1, A2, A3, a4, A5);
return 0;
}
B1018 锤子剪刀布
题目要求输出甲乙获胜,平局,输了的次数,还要输出获胜最多的手势。
按照字典序 B布 C锤子 J剪刀 顺序布赢锤子,锤子赢剪刀,剪刀赢布,用mp数组存对应的手势,mp[0] = 'B',
times_A[3], times_B[3]分别存甲乙,胜场,平场,输场次数。hand_A[3]和hand_B[3]存甲乙布,锤子,剪刀胜利次数。

c++
#include <iostream>
using namespace std;
char mp[3] = {'B', 'C', 'J'};
int n;
int times_A[3], times_B[3];
int hand_A[3], hand_B[3];
char c1, c2;
int k1, k2;
int change(char c)
{
if(c == 'B') return 0;
if(c == 'C') return 1;
if(c == 'J') return 2;
}
int main()
{
cin >> n;
while(n --)
{
cin >> c1 >> c2;
k1 = change(c1);
k2 = change(c2);
if((k1 + 1) % 3 == k2)//甲获胜
{
times_A[0] ++;
times_B[2] ++;
hand_A[k1] ++;
}
else if(k1 == k2)//平局
{
times_A[1] ++;
times_B[1] ++;
}
else
{
times_A[2] ++;
times_B[0] ++;
hand_B[k2] ++;
}
}
printf("%d %d %d\n", times_A[0], times_A[1], times_A[2]);
printf("%d %d %d\n", times_B[0], times_B[1], times_B[2]);
int id1 = 0, id2 = 0;
for(int i = 0; i < 3; i ++)
{
if(hand_A[i] > hand_A[id1]) id1 = i;
if(hand_B[i] > hand_B[id2]) id2 = i;
}
printf("%c %c\n", mp[id1], mp[id2]);
return 0;
}
A1042 Shuffling Machine


#include <iostream>
using namespace std;
const int N = 60;
char mp[5] = {'S', 'H', 'C', 'D', 'J'};
int Start[N], End[N], Next[N];
int k;
int main()
{
cin >> k;
for(int i = 1; i <= 54; i ++) Start[i] = i;
for(int i = 1; i <= 54; i ++) cin >> Next[i];
for(int step = 1; step <= k; step ++)
{
for(int i = 1; i <= 54; i ++)
{
End[Next[i]] = Start[i];
}
for(int i = 1; i <= 54; i ++)
{
Start[i] = End[i];
}
}
for(int i = 1; i <= 54; i ++)
{
if(i != 1) printf(" ");
Start[i] --;
printf("%c%d", mp[Start[i] / 13], Start[i] % 13 + 1);
}
return 0;
}
B1046 Shortest Distance

前缀和思想 O(n)
c++
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int dis[N];
int n, m;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
dis[i] += dis[i - 1] + a[i];
}
cin >> m;
while(m --)
{
int l, r;
cin >> l >> r;
if(l > r) swap(l , r);
int res = dis[r - 1] - dis[l - 1];
cout << min(res, dis[n] - res) << endl;
}
return 0;
}
1065 A+B and C (64bit)
溢出理解
在补码加法运算中,会出现溢出这种问题,可以将溢出理解为"循环补码"
比如三位机器数的加法,3+3 = 011 + 011 = 110 = -2
因此,我们可以得到这样的规律:
只有"正数+正数"才会上溢------正+正=负
只有"负数+负数"才会下溢------负+负=正
计组加减运算原理相关文章加减运算&溢出判断(计算机组成原理16)_单符号位判断溢出-CSDN博客


进行溢出判断
c++
#include <iostream>
using namespace std;
typedef long long LL;
int n;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
LL a , b, c;
cin >> a >> b >> c;
LL res = a + b;
bool flag = false;
if(a < 0 && b < 0 && res >= 0) flag = false;
else if(a > 0 && b > 0 && res < 0) flag = true;
else if(res > c) flag = true;
else flag = false;
if(flag)
{
printf("Case #%d: true\n", i);
}
else
{
printf("Case #%d: false\n", i);
}
}
return 0;
}
B1010 一元多项式求导
经过测试,不存在n<0的测试点,多项式中只有常数项,输出0, 0即可,其他情况只需要输出常数项之前的求导
c++
#include <iostream>
using namespace std;
int main() {
int a, b, flag = 0;
while (cin >> a >> b) {
if (b != 0) {
if (flag == 1) cout << " ";
cout << a * b << " " << b - 1;
flag = 1;
}
}
if (flag == 0) cout << "0 0";
return 0;
}
胡凡老师的思路:

c++
#include <iostream>
using namespace std;
const int N = 1010;
int a[N];
int main()
{
int k, e, count = 0;
while(cin >> k >> e)
{
a[e] = k;
}
a[0] = 0;
for(int i = 1; i <= 1000; i ++)
{
a[i - 1] = a[i] * i;
a[i] = 0;
if(a[i - 1] != 0) count ++;
}
if(count == 0) cout << "0 0";
else
{
for(int i = 1000; i >= 0; i --)
{
if(a[i] != 0)
{
printf("%d %d", a[i], i);
count --;
if(count != 0) printf(" ");
}
}
}
return 0;
}
B1031 查验身份证
题意:计算身份证前17位,带权和,mod 11 求得效验码和18位,比较,输出校验失败的身份证号码,全部校验成功输出All passed
思路:weight数组存权重,Z数组存需要哈希的校验码,X存为10,校验时特判一下,X为10的情况即可
c++
#include <iostream>
using namespace std;
const int N = 20;
int weight[N] = {7, 9, 10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
int Z[N] = {1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2};
int cnt;
int n;
string card;
int main()
{
cin >> n;
while(n --)
{
cin >> card;
int res = 0;
for(int i = 0; i < 17; i ++)
{
res += (card[i] - '0') * weight[i];
}
int m = Z[res % 11];
if(m != 10 && m != card[card.size() - 1] - '0')
{
cout << card << endl;
cnt ++;
}
else if(m == 10 && card[card.size() - 1] != 'X')
{
cout << card << endl;
cnt ++;
}
}
if(cnt == 0) puts("All passed");
return 0;
}
A1002 A+B for Polynomials
题意: 计算A + B 多项式,输出非零项数目,然后按照幂次从高到低依次输出系数。
思路:a[i]数组存幂次为i的项的系数,每次直接加进去,is_use数组判断每个幂次是不是被用到了。注意,计算数目的时候需要特判两项相加为0的情况。
c++
#include <iostream>
using namespace std;
const int N = 1010;
int n;
double a[N];
bool is_use[N];
int cnt = 0;
int main()
{
int t = 2;
while(t --)
{
cin >> n;
while(n --)
{
int idx;
double value;
cin >> idx >> value;
a[idx] += value;
is_use[idx] = true;
}
}
for(int i = 0; i <= 1000; i ++)
{
cnt += (is_use[i] && a[i] != 0);
}
cout << cnt;
for(int i = 1000; i >= 0; i --)
{
if(is_use[i] && a[i] != 0)
{
printf(" %d %.1lf", i, a[i]);
}
}
return 0;
}
B1009 Product of Polynomials
题意:计算两个多项式乘积
a[idx]存x的idx次方对应的系数,b[idx]存x的idx次方对应的系数,c存储答案,两重循环计算答案即可。
注意:两个多项式求积,指数是2*N级别,输出时需要注意,题目不难,细节扣了一会,30min左右完成。
c++
#include <iostream>
using namespace std;
const int N = 2020;
double a[N], b[N], c[N];
bool is_use[N];
int cnt, k;
int n;
int main()
{
cin >> n;
while(n --)
{
int idx;
double value;
cin >> idx >> value;
a[idx] = value;
k = max(k, idx);
}
cin >> n;
while(n --)
{
int idx;
double value;
cin >> idx >> value;
b[idx] = value;
for(int i = 0; i <= k; i ++)
{
c[idx + i] += b[idx] * a[i];
}
}
for(int i = 0; i <= 2000; i ++)
{
cnt += (c[i] != 0);
}
cout << cnt;
for(int i = 2000; i >= 0; i --)
{
if(c[i] != 0)
{
printf(" %d %.1lf", i, c[i]);
}
}
printf("\n");
return 0;
}
查找元素

题号 | 题目 | 分数 |
---|---|---|
B1041 | 考试座位号 | 15✔️ |
B1004 | 成绩排名 | 20✔️ |
B1028 | 人口普查 | 20✔️ |
B1041 考试座位号
题意:给出考生准考证号,试机号和考试号,要求根据试机号输出准考证和考试号。
思路:string数组query存储试机号对应的信息,细节,可以把准考证号和座位号字符串拼接,这样输出比较方便。
考虑存储O(n),查询的效率O(1)
c++
#include <iostream>
using namespace std;
const int N = 1010;
string query[N];
int n, m;
int main()
{
cin >> n;
while(n --)
{
string id, exam;
int test;
cin >> id >> test >> exam;
id += " " + exam;
query[test] = id;
}
cin >> m;
for(int i = 1; i <= m; i ++)
{
int x;
cin >> x;
cout << query[x] << endl;
}
return 0;
}
B1004 成绩排名
string name, id;
int score;
int max_score, min_score = 1000;
string max_info, min_info;
模拟比较成绩就行。
c++
#include <iostream>
using namespace std;
string name, id;
int score;
int max_score, min_score = 1000;
string max_info, min_info;
int n;
int main()
{
cin >> n;
while(n --)
{
cin >> name >> id >> score;
name += " " + id;
if(score > max_score)
{
max_score = score;
max_info = name;
}
if(score < min_score)
{
min_score = score;
min_info = name;
}
}
cout << max_info << endl;
cout << min_info << endl;
return 0;
}
B1028 人口普查
因为看错题目意思想的屎山代码
c++
#include <bits/stdc++.h>
using namespace std;
string name, birth;
int max_age, min_age = 30000000;
string max_age_name, min_age_name;
int n, age;
int months[13] = {0, 31, 28, 31,30,31,30,31,31,30,31,30,31};
int cnt = 0;
int main()
{
cin >> n;
while(n --)
{
cin >> name >> birth;
string date;
for(int i = 0; i < birth.size(); i ++)
{
if(birth[i] != '/')
date += birth[i];
}
age = stoi(date);
// cout << age << endl;
if(age < 18140906 || age > 20140906) continue;
// cout << age << endl;
if(age > max_age)
{
max_age = age;
max_age_name = name;
}
if(age < min_age)
{
min_age = age;
min_age_name = name;
}
int year = age / 10000;
int month = age / 100 % 100;
int day = age % 100;
if((year % 4 == 0 && year% 100 != 0) || year% 400 == 0) months[2] =29;
else months[2] = 28;
if(day <= months[month]) cnt ++;
else cout << age << endl;
}
if(cnt)
{
cout << cnt << " " << min_age_name << " " << max_age_name << endl;
}
else
{
cout << 0 << endl;
}
}
每天两题,持续更新中~
另外:自制PAT做题倒计时插件自制 PTA(拼题A)平台 计时器浏览器插件