一、0握手问题 - 蓝桥云课(条件枚举)
直接算代码:
#include <iostream>
using namespace std;
int main()
{
int sum=0;
for(int i=49;i>=7;i--)
sum+=i;
cout<<sum<<endl;
return 0;
}
验证代码:
int ans = 0;
for (int i = 1; i <= 50; ++i) {
for (int j = i + 1; j <= 50; ++j) {
if (i <= 7 && j <= 7) continue;
else ans ++;
}
}
模拟逻辑:前7个人不会相互握手,其他人相互握手,每次握手一次,次数加一。
二、 0门牌制作 - 蓝桥云课(十进制整数拆解)
直接算代码:
#include <iostream>
using namespace std;
int main()
{
int ans=0;
for(int i=1;i<=2020;i++)
{
int ans=i;
if(i%10==2) ans++;
if(i/10%10==2) ans++;
if(i/100%10==2) ans++;
if(i/1000==2) ans++;
// while (x > 0)
// {
// if (x % 10 == 2) ans ++;
// x /= 10;
// }
}
cout<<ans;
return 0;
}
知识点:
考察十进制整数拆解,即将一个十进制整数 xx 按照十进制拆除每一位,如 903903,拆成 {9,0,3}、{9,0,3}。拆解十进制整数的代码模版:
int a[count], len = 0;//数组中存储整数 xx 的每一位。len 代表 xx 的长度
do {
a[++len] = x % 10;
x /= 10;
} while (x > 0);
三、0九进制转十进制 - 蓝桥云课 (进制转换)
直接算代码:
#include <iostream>
using namespace std;
int main()
{
cout<<2*9*9*9+2*9+2;
return 0;
}
1、模拟进制转换过程,得到 n 进制转换为十进制的代码:
/**
* n 代表整数的进制
* a 存储n进制数每一位的数组
* len a数组长度,即该n进制整数的长度
*/
int n_to_10(int n, int a[], int len) {
int res = 0;
int p = 1;
for (int i = len - 1; i >= 0; --i) {
res += a[i] * p;
p *= n;
}
return res;
}
优化:
/**
* n 代表整数的进制
* a 存储n进制数每一位的数组
* len a数组长度,即该n进制整数的长度
*/
int n_to_10(int n, int a[], int len) {
int res = 0;
for (int i = 0; i < len; ++i) {
res = res * n + a[i];//将当前的 res 左移一位(相当于乘以 n),然后加上当前位的值
}
return res;
}
2、模拟十进制数转换为 n 进制
/**
* 十进制转换为n进制
* x为十进制整数
* a为转换后的存储空间,逆序存储
* 该函数返回一个len,代表转换后的长度
*/
int _10_to_n(int x, int n, int a[]) {
int len = 0;
do {
a[len ++] = x % n;
x /= n;
} while (x > 0);
return len;
}
四、0平方差 - 蓝桥云课(高精度计算)
算法代码(模拟):
#include <iostream>
#include <string.h>
using namespace std;
char s1[105], s2[105]; // 存放输入的两个大整数字符串
int a1[105] = {0}, a2[105] = {0}; // 存放将字符串转换为整数后的数组
int result1[105], result2[105], result[210] = {0}; // 存放加法、减法和乘法的结果
int main()
{
scanf("%s %s", s1, s2); // 读取两个大整数字符串
int i1 = strlen(s1); // 获取第一个字符串的长度
int i2 = strlen(s2); // 获取第二个字符串的长度
int l1 = i1, l2 = i2, begin1 = 0, begin2 = 0; // l1为|A|的长度, l2为|B|的长度
// 处理负号
if (s1[0] == '-') {
l1--; // 如果第一个数是负数,长度减1
begin1 = 1; // 从第二个字符开始处理
}
if (s2[0] == '-') {
l2--; // 如果第二个数是负数,长度减1
begin2 = 1; // 从第二个字符开始处理
}
// 将字符串转换为整数数组,个位存放在数组首地址处
for (int i = begin1; i < i1; i++) {
a1[i1 - 1 - i] = s1[i] - '0';
}
for (int i = begin2; i < i2; i++) {
a2[i2 - 1 - i] = s2[i] - '0';
}
int maxl = l1; // 找出两个数的最大长度
if (l2 > l1)
maxl = l2;
// 加法部分
int r1 = maxl; // 加法的结果长度
for (int i = 0; i < maxl; i++) { // 先直接加
result1[i] = a1[i] + a2[i];
}
for (int i = 0; i < maxl; i++) { // 后进位
if (result1[i] >= 10) {
result1[i] %= 10;
result1[i + 1] += 1;
if (i == maxl - 1)
r1++;
}
}
// 减法部分
int r2 = maxl; // 减法的结果长度
int flag1 = 0; // 标记结果是否为负数
for (int i = 0; i < r2; i++) { // 先直接减
result2[i] = a1[i] - a2[i];
}
while (result2[r2 - 1] == 0) // 将值为0的高位从长度中去除
r2--;
if (result2[r2 - 1] < 0) { // 若最高位为负,则结果是一个负值
flag1 = 1;
for (int i = 0; i < r2; i++) {
result2[i] *= (-1); // 将各个位取相反数
}
}
for (int i = 0; i < r2; i++) { // 进位
if (result2[i] < 0) {
result2[i] = 10 + result2[i];
result2[i + 1] -= 1;
}
}
while (result2[r2 - 1] == 0) // 去除前导零
r2--;
// 乘法部分
int r = r1 + r2; // 乘法的结果长度
for (int i = 0; i < r1; i++) { // 乘法定律
for (int j = 0; j < r2; j++) {
result[i + j] += result1[i] * result2[j];
}
}
for (int i = 0; i < r; i++) { // 进位
if (result[i] >= 10) {
result[i + 1] += result[i] / 10;
result[i] %= 10;
}
}
// 打印结果
while (result[r - 1] == 0) { // 计算结果的真正长度
r--;
}
if (flag1 == 1) // 如果结果是负数,打印负号
printf("-");
for (int i = r - 1; i >= 0; i--) { // 从高位到低位打印结果
printf("%d", result[i]);
}
return 0;
}
大佬代码(复杂了点):
#include <stdio.h>
#include <string.h>
const int N = 300; // 定义常量N,表示数组的最大长度
char A[N], B[N]; // 用于存储输入的两个字符串形式的整数
int a[N], lena, f_a; // a存储A的绝对值,lena是A的长度,f_a是A的符号(1表示负数)
int b[N], lenb, f_b; // b存储B的绝对值,lenb是B的长度,f_b是B的符号(1表示负数)
int aa[N], lenaa; // aa存储a的平方
int bb[N], lenbb; // bb存储b的平方
int c[N], lenc; // c存储最终结果
/**
* 判断a是否大于等于b,如果成立,返回1,否则返回0
* a 存储a整数的数组
* lena a整数的长度
*/
int comp(int a[], int lena, int b[], int lenb) {
if (lena != lenb) return lena > lenb; // 如果长度不同,直接比较长度
for (int i = lena - 1; i >= 0; --i) { // 从最高位开始比较
if (a[i] > b[i]) return 1;
else if (a[i] < b[i]) return 0;
}
return 1; // 如果所有位都相等,返回1
}
/**
* 计算出 a - b,并且放在c中,保证 a >= b
* 返回c的长度
*/
int sub(int a[], int lena, int b[], int lenb, int c[]) {
int lenc = lena; // 初始化结果的长度为a的长度
for (int i = 0; i < lenc; ++i) {
c[i] = a[i] - b[i]; // 逐位相减
}
for (int i = 0; i < lenc; ++i) {
if (c[i] < 0) c[i] += 10, c[i + 1] --; // 处理借位
}
while (lenc > 0 && c[lenc - 1] == 0) lenc --; // 去掉前导零
if (lenc == 0) lenc = 1; // 如果结果为0,保留一位
return lenc;
}
/**
* 计算出 a * b,并且放在c中
* 返回c的长度
*/
int multi(int a[], int lena, int b[], int lenb, int c[]) {
int lenc = lena + lenb - 1; // 初始化结果的长度
for (int i = 0; i < lena; ++i) {
for (int j = 0; j < lenb; ++j) {
c[i + j] += a[i] * b[j]; // 逐位相乘并累加
}
}
for (int i = 0; i < lenc; ++i) {
if (c[i] > 9) { // 处理进位
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
}
if (c[lenc] > 0) { // 处理最高位的进位
c[lenc + 1] += c[lenc] / 10;
c[lenc] %= 10;
lenc ++;
}
return lenc;
}
/**
* 将字符串转换为整数存储,并且返回整数长度
*/
int get_num(char *s, int *a) {
int n = strlen(s); // 获取字符串长度
for (int i = n - 1; i >= 0; --i) a[n - 1 - i] = s[i] - '0'; // 将字符转换为数字并逆序存储
return n;
}
void sol() {
scanf("%s", A); // 读取第一个数
scanf("%s", B); // 读取第二个数
if (A[0] == '-') lena = get_num(A + 1, a), f_a = 1; // 如果A是负数,处理符号并获取绝对值
else lena = get_num(A, a); // 如果A是正数,直接获取绝对值
if (B[0] == '-') lenb = get_num(B + 1, b), f_b = 1; // 如果B是负数,处理符号并获取绝对值
else lenb = get_num(B, b); // 如果B是正数,直接获取绝对值
lenaa = multi(a, lena, a, lena, aa); // 计算a的平方
lenbb = multi(b, lenb, b, lenb, bb); // 计算b的平方
if (comp(a, lena, b, lenb)) { // 如果a的绝对值大于等于b
lenc = sub(aa, lenaa, bb, lenbb, c); // 计算a² - b²
} else { // 否则结果是一个负数
lenc = sub(bb, lenbb, aa, lenaa, c); // 计算b² - a²
printf("-"); // 输出负号
}
for (int i = lenc - 1; i >= 0; --i) {
printf("%d", c[i]); // 输出结果
}
printf("\n");
}
int main() {
sol(); // 调用sol函数
}
知识点:
在C/C++中,由于没有大整数类,最大的整数只能表示 2^64−1,所以在一些情况下,需要模拟大整数运算,常用的其实就是乘法和加减法。
由于本身就是模拟运算,所以按照手算的竖式运算即可。
对于大整数运算通常有以下几点注意:
由于输入的时候无法使用正常方式输入,所以我们采用字符串输入,然后逆序过来,方便运算。
加法的话直接对应位置相加,然后从低位到高位处理进位问题。
减法的话,需要先判断大小,然后用绝对值大的整数减去小的,最后处理借位和符号问题。
乘法的话,按照竖式的方式进行计算,然后处理进位问题。
五、2.跑步 - 蓝桥云课(日期问题)
算法代码:
#include <stdio.h>
// 每个月的日期数,ds[i]代表i月份的天数
int ds[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main() {
int sy = 2022; // 起始年份
int ey = 2022; // 结束年份
int week = 6; // 定义 0-6 ,0为星期日,1为星期一,2为星期二...
int ans = 0;
for (int y = sy; y <= ey; ++y) { // 第一层循环,枚举年份
for (int m = 1; m <= 12; ++m) { // 第二层循环,枚举月份
int dd = ds[m];
if (y % 4 == 0 && y % 100 != 0 && m == 2) // 判断是不是闰月
dd = 29;
else if (y % 400 == 0 && m == 2) // 判断是不是闰月
dd = 29;
for (int d = 1; d <= dd; ++d) { // 枚举天
if (week == 0 || week == 6 || d % 10 == 1) { // 如果满足某一个条件,就记录答案
ans ++;
}
week = (week + 1) % 7; // 向后推移星期几
}
}
}
printf("%d\n", ans);
return 0;
}
思路:(通用框架)
使用列表存储月份的天数,使得判断天数的时候能够直接查表,查表法非常好用,后续算法中也可能会用到。
星期的推演,由于星期是7天为一个轮回,所以使用取模的技巧,将周日设置为0,巧妙的将周六后是周日、周日后一天是周一联系的起来。
观察到每个日期是以1结尾,所以,利用取模的技巧将最后一个数取出进行判断,舍去了需要判断是1、11、21、31的麻烦。