蓝桥杯day1幸运数---2.26

目录

1.幸运数

法一:暴力枚举

1.为什么要先转换为字符串?

法二:凑

1.为什么是a[5][50]?

2.为什么要用三层for循环? 

3.为什么是count += a[i][j] * a[k][j];  相乘?

 4.用来计算一个数各个位的数字之和的代码模板:

5.注意主函数中的循环的边界

总结: 



本文中一些来自讯飞星火。


1.幸运数

题目链接:3.幸运数 - 蓝桥云课 (lanqiao.cn)

法一:暴力枚举

有点慢,但是能出来,反正是填空题,只要结果。

#include<bits/stdc++.h>
using namespace std;
inline bool check(int x){
    string s;
    while(x) {
		s += (char)(x % 10 + '0'), x /= 10; //详细解释见下文
	} 
    int n = s.size();
    if(n % 2 == 1)  return false;     //约束1:偶数个数位
    int sum1 = 0, sum2 = 0;
    for(int i = 0; i < n / 2; i++){  //详细解释见下文 
        sum1 += s[i] - '0';
        sum2 += s[i + n / 2] - '0';
    }
    return sum1 == sum2;            //约束2:前半部分和等于后半部分
}
int main(){
     int ans = 0;
     for(int i = 1; i <= 100000000; i++)
         if(check(i))  ans++;
     cout<<ans<<endl;
    return 0;
}

1.为什么要先转换为字符串?

这段代码将整数转换为字符串是为了方便处理数字的各个位数。通过将整数转换为字符串,可以逐个访问每个位上的数字,并进行相应的计算和比较操作。

在这段代码中,首先使用循环将整数 `x` 的每一位数字提取出来,并将其转换为字符形式存储在字符串 `s` 中。然后,通过字符串的长度 `n` 来判断数字的位数是否为偶数,如果不满足约束条件,则直接返回 `false`。

接下来,通过循环遍历字符串的前半部分和后半部分,分别计算它们的数字之和。最后,判断前半部分和后半部分的数字之和是否相等,如果相等,则返回 `true`,否则返回 `false`。

因此,将整数转换为字符串是为了方便对数字的各个位数进行处理和计算。

2.一小段代码的具体解释:

for(int i = 0; i < n / 2; i++){
        sum1 += s[i] - '0';
        sum2 += s[i + n / 2] - '0';
    }

这段代码是一个循环,用于计算字符串`s`的前半部分和后半部分的数字之和。

具体解释如下:
- `for(int i = 0; i < n / 2; i++)` 表示从索引 `0` 开始,到索引 `n / 2`(不包括)结束,执行循环。这里 `n` 是字符串 `s` 的长度。
- `sum1 += s[i] - '0';` 表示将字符串 `s` 中索引为 `i` 的字符减去字符 `'0'`,然后将结果加到变量 `sum1` 中。这里使用了字符与整数之间的转换,将字符形式的数字转换为对应的整数值。
- `sum2 += s[i + n / 2] - '0';` 表示将字符串 `s` 中索引为 `i + n / 2` 的字符减去字符 `'0'`,然后将结果加到变量 `sum2` 中。这实际上是计算字符串 `s` 的后半部分的数字之和。

这段代码通过循环执行上述操作,直到遍历完字符串 `s` 的前半部分,从而计算出前半部分和后半部分的数字之和,并将它们分别存储在变量 `sum1` 和 `sum2` 中。

法二:凑


#include<bits/stdc++.h>
using namespace std;

int a[5][50] ={0}; // 定义数组a[i][j]表示i位数和为j,j最大为9*i; //详细解释见下文
int nume=0;

void num(int x) {
	int t=0; //t表示位数 
	int sum=0; //sum表示每位的数字之和
	while(x){ //这个循环主要就是用来求各个位上的数字之和的
		sum+=x%10; // 取x的个位数加到sum中
		x/=10; // 将x除以10,相当于去掉x的个位数
		t++; //计数:数字的位数+1 
	} 
	a[t][sum]++; //符合t位且和为sum的数字的个数加1 
}

int main() {
	for(int i=1; i<10000; i++) {
		num(i); //调num函数
	}
	for(int i=1; i<=4; i++) { //限制左边一半的位数
		for(int j=1; j<=9*i; j++) { //限制各个位数的和
			for(int k=1; k<=i; k++) { //限制右边一半的位数,当比左边小的时候
//右边这一位的元素是0,
//比如0位的和为1的数是不存在的,所以a[0][1]=0// 右一半只要比i小前面补0
				nume+=a[i][j]*a[k][j]; //计算符合条件的个数  //详细解释见下文 
			}
		}
	}
	printf("%d",nume);
	return 0;
}

1.为什么是a[5][50]?

二维数组在C语言中是由多个一维数组构成的数组,它可以用来存储和操作二维数据结构。

在这段代码中,二维数组`a[5][50]`的定义有其特定的目的:

- **第一维的大小为5**:这是因为我们考虑的数字最大为9999,即四位数,而数组的第一维用来表示数字的位数,所以最大的位数是4,但数组是从0开始索引的,所以需要一位额外的空间,因此定义为5。
- **第二维的大小为50**:这是因为我们关心的是数字的各位数字之和,最大可能的和是9+9+9+9=36,但考虑到数组从0开始索引,所以需要一个大小为50的数组来存储0到36的所有可能和,再加上一些额外的空间。

这样的设计使得数组`a`能够有效地记录1到9999之间每个数字的位数和出现的次数。通过这种结构,代码可以快速地查找和更新每个数字的位数和,从而计算出最终的结果。

2.为什么要用三层for循环? 

这段代码是一个嵌套循环,用于计算一个二维数组 `a` 中的元素相乘并累加到变量 `count` 中。

具体解释如下:
- 外层循环 `for (int i = 1; i <= 4; i++)` 控制左一半的范围,从 1 到 4。
- 中间循环 `for (int j = 1; j <= i * 9; j++)` 控制右一半的范围,从 1 到 `i * 9`。
- 内层循环 `for (int k = 1; k <= i; k++)` 控制右一半的每个元素与左一半对应位置的元素相乘,并将结果累加到 `count` 中。

这段代码的作用是遍历二维数组 `a` 中的部分元素,将满足条件的元素相乘并累加到 `count` 中。

3.为什么是count += a[i][j] * a[k][j];  相乘?

在这段代码的上下文中,相乘的操作是为了计算两个不同位数的数字组合的数量,这两个数字的位数和相等。这是解决特定问题的一部分,即找出所有位数和相等的数字对。

具体来说,`a[i][j]` 表示位数为 `i` 的数字中,位数和为 `j` 的数字的出现次数。同样,`a[k][j]` 表示位数为 `k` 的数字中,位数和为 `j` 的数字的出现次数。

当代码执行 `count += a[i][j] * a[k][j];` 时,它实际上是在计算所有位数和为 `j` 的数字对的数量,其中一个数字的位数为 `i`,另一个数字的位数为 `k`。这里的乘法是因为每找到一个位数和为 `j` 的 `i` 位数字,就可以与任何位数和为 `j` 的 `k` 位数字配对,形成一对数字和相等的数字对。

最终,`count` 变量将包含所有可能的位数和相等的数字对的总数量。这是通过遍历所有可能的 `i` 和 `k` 的组合并累加它们的乘积来实现的。

 4.用来计算一个数各个位的数字之和的代码模板:

while (x) { // 这个循环主要就是用来求各个位上的数字之和的
    sum += x % 10; // 取x的个位数加到sum中
    x /= 10; // 将x除以10,相当于去掉x的个位数
}

这段代码是一个循环,用于计算一个数字的位数和。

具体解释如下:
- `while (x)` 表示当 `x` 不为 0 时执行循环。
- `sum += x % 10;` 表示将 `x` 的个位数加到变量 `sum` 中。
- `x /= 10;` 表示将 `x` 除以 10,相当于去掉 `x` 的个位数。

这段代码通过循环执行上述操作,直到 `x` 变为 0,从而计算出一个数字的各个位上的数字之和,并将结果存储在变量 `sum` 中。

5.注意主函数中的循环的边界

因为只看一半,所以是根号大小,也就是根号100000000,是10000。

可以写(1,9999]也可以写(1,10000)。


总结: 

1.整数转换为字符串可以方便对数字的各个位数进行处理和计算。因为字符串就相当于数组了。可以通过下标来快速访问每个元素。

注意怎么方法:数字和字符串如何相互转换? 

char类型的保存形式是ASCII码数值,因此将数字转换为字母就是转换为数字对应的字符的ASCII。

也就是

数字---->字符:+'0'     

字符---->数字:相反

2.二维数组不仅仅可以代表行和列,这只是最基本的用法。比如在法二中就定义了二维数组`a[5][50]`,其第一个数代表位数,第二个数代表各个位的数字之和。

3.100000000是不符合要求的,所以最多遍历到99999999。方法二的思路是一半一半的凑,把9999999分成两半,每一半的位数最多是9999。

i<=4这里的i遍历的就是左半边的位数,所以是小于等于4的

第二层for循环遍历的是左半边各位数字之和,最大就是这i位都为9,那么值就是i*9


现在我要找右边那一半是多少,按理说左边位数确定了,右边和他一样就行了,不用遍历,但是这样做的话我就找不到类似1001这样的数字了,因为这个数字左半边值是10,右半边值是1,也就是说满足题意的数字,并不要求左右半边相等,不相等,也就是右边比左边位数少的时候,我补0就可以了。1001就是右半边1的补了0

如果没有第三层遍历,我只找了和左边位数一样的右边就是没有补0,如果有第三层遍历,我允许右边位数比左边位数小的情况存在,就是补了

它不是显式的有一个操作我把1的左边加个0,而是我认为10和1能够凑成一个合法数字,说明我把10和1凑一起的时候是1001这样凑的而不是101这样凑
a[i][j]*a[k][j]所以这个就是符合i位且和为j的所有数的个数和一个符合k位且和为j的所有数的个数相乘。

4.注意法二中那个全局变量nume不能用count,因为有命名变量冲突什么的,总之用点不一样的,避免这种情况的发生。

5.最后理一下思路:

(1)方法一就是暴力枚举每个数字,看每个数字符不符合前面一半位数的数字的和等于后面一半的位数之和。在判断这个之前还要看看是不是偶数位。

(2)方法二是定左边一半的数字,然后去寻找右边的一半。要注意考虑位数可以不同但是和相同,也就是有0的情况。

6.代码都是抽象的,有时候举个例子会更好理解一点,任重道远,加油!

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值