LuckyNumber:数位相同至多两位的数

    科技2025-11-26  13

    给定一个数,要给出小于等于这个数的所有非负数中,各个数位上的数字重复不超过两次的数的个数。

    比如111,110,112,121,1211都是,102,120,1200就不是。

    ★题目描述

    YY 的幸运数字是......,是什么数字我也不知道,但是已知这个数字的十进制表示(不含前导零)中只包含不超过两个不同的数字。

    给定一个数 nn ,请计算出不超过 nn 的所有正整数中,有可能是 YY 的幸运数字的个数。

    ★输入

    输入一行,包含一个正整数 nn。

    对于 60% 的数据:

    1≤n≤10的5次方,

    对于 100% 的数据:

    1≤n≤10的9次方。

    ★输出

    输出一个整数,表示不超过 nn 的所有正整数中,有可能是 YY 的幸运数字的个数。

    ★输入样例

    103

    ★输出样例

    101

    ★样例解释

    不超过103的所有正整数中,只有102、103不可能是 YY 的幸运数字。

    代码:

    #include<iostream> #include<stdio.h> using namespace std; #define sc scanf #define ci cin #define co cout #define e endl #define Maxx(a,b) (a>b?a:b) #define Minn(a,b) (a<b?a:b) #define lld long long int int getLen(int n){ if(n==0) return 1; int len=0; while(n){ n/=10; len++; } return len; } int cal(lld n){ int dict[10] = {0,9,99,351,927,2151,4671,9783,20079,40743}; lld dig[10] = {0,1,10,100,1000,10000,100000,1000000,10000000,100000000}; int mon[9] = {1,2,4,8,16,32,64,128,256};//与以下的binary数组相配合,记录操作次数用的 int len,highest,value; len = getLen(n);//取得位数 highest = n/dig[len];//取得最高位 value += dict[len-1] + (dict[len]-dict[len-1])/9*(highest-1);//len-1位的情况全部包括进来,最高位从1到highest-1的情况全部包括进来 //对最高位为highest的数进行枚举 lld res; for(int j=0;j<=9;j++){//从左往右数第二位 从0到9枚举 //co<<j<<e; int binary[9] = {0,0,0,0,0,0,0,0,0};//二进制位类似于状态位 if(j!=highest){//只取和最高位不相同的数,因为相同的数必然会包含在这些不同的数中 //220: 枚举 0: 四类情况 200 202 220 222 // 枚举 1: 四类情况 211 212 221 222 // 枚举 2: 四类情况 222 222 222 222 // 枚举 3: 四类情况 233 232 223 222 // 枚举 4: 四类情况 244 242 224 222 // 枚举 5: 四类情况 255 252 225 222 // 枚举 6: 四类情况 266 262 226 222 // 枚举 7: 四类情况 277 272 227 222 // 枚举 8: 四类情况 288 282 228 222 // 枚举 9: 四类情况 299 292 229 222 //只需跳过枚举2并且第四列的数据删到只剩一个,剩下的就是可能出现的枚举,在和数据比大小,最后一列全部相同的情况总共出现9次 //先挑出所有的比220小的数,再判断222是否小于220,大于就不操作,小于等于就要减掉多加上的8次 int cnt = 0; while(1){//不论与N的大小全部枚举出来,暴力解法 res=highest*dig[len]; for(int i=8;i>=10-len;i--){ if(binary[i]==0) res += dig[9-i]*j; else res += dig[9-i]*highest; } if(res<=n){ value++;//只要res小于等于n就加进来 } for(int i=8;i>=10-len;i--){//+1操作 if(binary[i]==0){ binary[i] = 1; break; }else{ binary[i] = 0; } } cnt++; if(cnt==mon[len-1]) break; //操作2^(len-1)方停止 } } }//最后的res是全1的类型 if(res<=n) value -= 8; return value; } int main(){ lld n; int dict[10] = {0,9,99,351,927,2151,4671,9783,20079,40743}; while(sc("%lld",&n)!=EOF){ if(n==1000000000){ co<<dict[9]+1<<e; }else if(n<=100){ co<<n<<e; }else{ co<<cal(n)<<e; } } return 0; }

    dict数组是通过推导得来,因为1位数的情况下,从一开始,就有9种符合条件,2位数以下的情况就包括10-->99再加上一位数的情况,总共99种,

    3位数就应该是100-->999:分两类情况讨论

    1类:各个数位都只用到1位相同的数字=9种

    2类:各个数位恰好都用到了两个数字{罗列一下

    100 101 110 112 113 114 115 116 117 118 119 121 122 131 133 141 144 151 155 161 166 171 177 181 188 191 199//27个

    200 202 211 212 220 221 223 224 225 226 227 228 229 ------------------------------//也是27个

    }

    这样子算出来就是=9+27*9 = 9 + 81*3 = 252    再加上2位数的情况 = 252 +99 = 351;

    以此类推4位数 = 351 + 9 + 81*7 = 927.....

    8位数 = 【7位数情况的个数】 + 9 + 81*(2的8次方-1);

    打出dict的代码:

    int dict[9];//创建字典存储对应位数可能有的情况,上限9位数就是1000000000 dict[1] = 9;//1位数9种 //co<<dict[1]<<e; for(int i=2;i<=8;i++){//n位数可能的情况是 n-1位数可能的情况加上9(恰好1个数字重复)再加上81*(2^(n-1)-1)【恰好两个数字重复】 cnt*=2; dict[i] = dict[i-1] + 9 + 81*(cnt-1); co<<dict[i]<<e; }

    算法里面是用binary数组来表示对应位置上的情况,比如说第八位数就是binary[8],存储0和1的表示方式来控制对应位置上可能存在的情况,因为之前最高位highest已被记录,那binary[8]=1就意味着第八位现在的状态是和最高位一致.

    Processed: 0.019, SQL: 9