双码二部数定义:由两个不同数码组成,每个数码多于1位时相连而不分开的正整数称为双码二部数,其中处于高位相连数字称为高部,处于低位相连数字称为低部;
例如330是一个3位双码二部数:高部数字为3,高部位数为2;低部数字为0,底部位数为1,而333只有一个数码,4407有三个数码,4474的数码4呈分开状态,都不是双码二部数;
试统计n位双码二部数的个数,并求出n位双码二部数从小到大排序序列的第m项;
输入正整数n(1< n< 10000)与m,输出n位双码二部数的个数,同时输出n位双码二部数序列的第m项的高部数字(高部位数)和低部数字(底部位数),如果m大于n位双码二部数的个数,则输出“0”;
1.递增枚举要点:
设n(n>1)位双码二部数为a……ab……b(1<=a<=9,0<=b<=9),其高部数字a有la位,低部数字b有lb位,显然有:
- la+lb=n (1<=la<=n-1)
为了确保从小到大枚举双码二部数,要注意枚举循环的先后次序:首先,高部数字a必须从小到大,范围为1~9;当a确定后,高部位数la简单地从小到大或从大到小都不能确保双码二部数从小到大变化,需要配合b分以下3步完成,为便于理解,以n=4,a=4的递增进程实施标注;
(1)、la增长(1~n-2)段,lb=n-la,b递增(0~a-1)取值;
4000 4111 4222 4333 (la=1, lb=3, b: 0~3)
4400 4411 4422 4433 (la=2, lb=2, b: 0~3)
(2)、la与lb取定值段,la=n-1,lb=1,b递增(0~9)取值(当b=a时跳过);
4440 4441 4442 4443 4445 4446 4447 4448 4449
(la=3, lb=1, b: 0~9, 其中b=4时跳过)
(3)、la减小(n-2~1)段,lb=n-la,b递增(a+1~9)取值;
4455 4466 4477 4488 4499 (la=2, lb=2, b: 5~9)
4555 4666 4777 4888 4999 (la=1, lb=3, b: 5~9)
以上3步骤中每一步骤都是递增的,且3个步骤衔接中没有重复与遗漏,从而可能确保n位的双码二部数从小到大递增,没有重复与遗漏;
2.枚举程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
int a,b,a0,b0,n,la,lb,la0;
long m,s;
printf("请输入整数n,m: ");
scanf("%d,%ld",&n,&m);
s=0;
for(a=1;a<=9;a++) /*高部数字a从小到大枚举*/
{
for(la=1;la<=n-2;la++) /*高部数字la分3个步骤枚举*/
{
lb=n-la;
for(b=0;b<=a-1;b++)
{
s++; /*变量s统计个数*/
if(s==m) /*记录第m个数的信息*/
{
a0=a;
b0=b;
la0=la;
}
}
}
la=n-1;
lb=1;
for(b=0;b<=9;b++)
{
if(a!=b) /*当a=b时跳过*/
{
s++;
if(s==m)
{
a0=a;
b0=b;
la0=la;
}
}
}
for(la=n-2;la>=1;la--)
{
lb=n-la;
for(b=a+1;b<=9;b++)
{
s++;
if(s==m)
{
a0=a;
b0=b;
la0=la;
}
}
}
}
if(s>=m)
printf(" %ld,%d(%d)%d(%d)\n",s,a0,la0,b0,n-la0);
else
printf(" %ld,%d\n",s,0);
}
3.简化枚举循环设计:
#include<stdio.h>
int main()
{
int a,b,a0,b0,n,la,la0;
long m,s;
printf("请输入整数n,m: ");
scanf("%d,%ld",&n,&m);
s=1;
a=1;
b=0;
la=1; /*默认n位第1个为1后n-1个0*/
while(la<n-1 || a<9 || b<8)
{
s++;
if(b==9) /*此时b不能增1,有以下两种选择*/
if(la==1) /*①a增1后,b从0开始*/
{
a++;
b=0;
}
else /*②a段长增1后,b从a+1开始*/
{
la--;
b=a+1;
}
else if(b!=a-1) /*a与la不变,b增1*/
b++;
else if(la!=n-1) /*a段长增1后,b从0开始*/
{
la++;
b=0;
}
else if(b<8) /*b增2跳过a=b情形*/
b+=2;
if(s==m)
{
a0=a;
b0=b;
la0=la;
}
}
if(s>=m)
printf(" %ld,%d(%d)%d(%d)\n",s,a0,la0,b0,n-la0);
else
printf(" %ld,%d\n",s,0);
}
4.程序运行示例及其注意事项:
请输入整数n,m: 40,2017
3159,6(30)7(10)
可知40位双码二位数共有3159个,40位双码二部数升序序列的第2017项高部30个“6”,低部10个“7”;
由第2017项的数据看似非常大,但枚举次数只有3159次;
枚举循环次数即n为双码二部数的个数,随着n的增大运算显著增长,但对于n<10000,枚举时间是可以接受的;
双码二部数的递增枚举是比较复杂的,也容易出错,这一设计提醒我们不要轻视枚举,枚举设计可以解决一些较为复杂的搜索案例;