幸运数字(蓝桥杯23省赛)

幸运数字(蓝桥杯23省赛)

题目分析

暴力判断的思路就不讲了,这道题主要想将一个思想,对于这种数字类的题目,对半枚举的思路。

100000000是不符合要求的,所以最多遍历到99999999。这个思路是我一半一半的凑,把9999999分成两半,每一半的位数最多是9999。这里的i遍历的就是左半边的位数,所以是小于等于4的。

 for (int i = 1; i <= 4; i++) {}

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

 for (int j = 1; j <= i * 9; j++) {

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

for (int k = 1; k <= i; k++)

然后我只需要这点位数为i和为j和位数为k和为j的数字的个数,然后让它们相乘即可。

 count += a[i][j] * a[k][j];

提问:但是怎么这个0才会出现呢,就是怎么才能补上?

如果我没有第三层遍历,我只找了和左边位数一样的右边就是没有补0,如果我有第三层遍历,我允许右边位数比左边位数小的情况存在,就是补了。它不是显式的有一个操作我把1的左边加个0,而是我认为10和1能够凑成一个合法数字,说明我把10和1凑一起的时候是1001这样凑的而不是101这样凑。

提问: a [ i ] [ j ] ∗ a [ k ] [ j ] a[i][j]*a[k][j] a[i][j]a[k][j]所以这个就是一个i位和为j的数和一个k位和为j的数相乘对吗?也就比如1322,就是13和22相乘吗或者是1001,是10和1相乘

不是的。举个例子。

a [ 3 ] [ 5 ] a[3][5] a[3][5]代表位数3各位数之和为5的数字个数,比如有122,221,212,203,230,302,320,140,104,410,401,500,113,131,311。所以 a [ 3 ] [ 5 ] = 15 a[3][5]=15 a[3][5]=15

a [ 2 ] [ 5 ] a[2][5] a[2][5]代表位数2各位数之和为5的数字个数,比如有23,32,50,14,41,所以 a [ 2 ] [ 5 ] = 5 a[2][5]=5 a[2][5]=5

那么 a [ 3 ] [ 5 ] ∗ a [ 2 ] [ 5 ] = 15 ∗ 5 = 75 a[3][5]*a[2][5]=15*5=75 a[3][5]a[2][5]=155=75,也就是找到了75个合法数字,他们分别是122023,122032,122050,122014,122041,221023,221032等。

根据上述例子,左半边和右半边的位数不同也没有关系,我在右半边数字的左边添0依然可以组成合法数字。

题目代码

public class Main {
	static int a[][] = new int[5][50]; // 定义数组a[i][j]表示i位数和为j,j最大为9*i;
	static int count = 0;	 
	static void num(int x) {
	    int t = 0, sum = 0; // t为位数,sum为各个位上的数字之和 
	    while (x>0) { //这个循环主要就是用来求各个位上的数字之和的 
	        sum += x % 10; // 取x的个位数加到sum中
	        x /= 10; // 将x除以10,相当于去掉x的个位数
	        t++; //计数:数字的位数+1 
	    }
	    a[t][sum]++; //和为sum的t位数,这个数的个数+1,数组加 
	}
	 
public static void main(String[] args) {
	    for (int i = 1; i <= 9999; i++) {
	        num(i); //调num函数 
	    }
	   // System.out.println(a[3][5] + " " + a[2][5]);
	    //
	    for (int i = 1; i <= 4; i++) { // 左一半,1~4   
	        for (int j = 1; j <= i * 9; j++) {//一半的和,最大就是这i位都为9,那么值就是i*9
	            for (int k = 1; k <= i; k++) { // 枚举右一半的位数,可以比左半边位数小
	                count += a[i][j] * a[k][j];  //
	            }
	        }
	    }
	    System.out.println(count);
}
}
#include <stdio.h>
int a[5][50] = {0}; // 定义数组a[i][j]表示i位数和为j,j最大为9*i;
int count = 0;
 
void num(int x) {
    int t = 0, sum = 0; // t为位数,sum为各个位上的数字之和 
    while (x) { //这个循环主要就是用来求各个位上的数字之和的 
        sum += x % 10; // 取x的个位数加到sum中
        x /= 10; // 将x除以10,相当于去掉x的个位数
        t++; //计数:数字的位数+1 
    }
    a[t][sum]++; //和为sum的t位数,这个数的个数+1,数组加 
}
 
int main() {
    for (int i = 1; i <= 9999; i++) {
        num(i); //调num函数 
    }
    //
    for (int i = 1; i <= 4; i++) { // 左一半,1~4 、
        for (int j = 1; j <= i * 9; j++) {//一半的和,最大就是这i位都为9,那么值就是i*9
            for (int k = 1; k <= i; k++) { // 枚举右一半的位数,可以比左半边位数小
                count += a[i][j] * a[k][j];  //
            }
        }
    }
    printf("%d", count);
    return 0;
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值