P数序列

定义十进制中没有重复数字的正整数为P数,指定p数按升序排列所得序列为指定P序列;

试求指定m位P数的个数,并求出m位P序列的第n项;

输入正整数m(2<=m<=10)和n(2<=n),输出m位P数的个数,同时输出m位P序列的第n项(若n大于m位P数的个数,则输出提示信息);

1.枚举设计:

(1)、枚举设计要点;

通过循环求得m位最小与最小的P数a、b,设置递增循环枚举a~b中的所有整数x,以确保序列为升序排序;

分离x的m个数字,并用数组f[k]统计数字k(0~9)的个数,判别:若f[k]>1(0~9),存在重复数字,返回;否则,f[k]<=1(0~9),不存在重复数字,x即为P数,用s统计其个数,并用变量z记录其中的第n个;

最后输出序列个数s和序列第n项z;

(2)、程序设计;

#include<stdio.h>
int main()
{
   int k,m,t,f[10];
   long a,b,n,s,w,x,z;
   printf("请输入m,n:");
   scanf("%d,%ld",&m,&n);
   a=10;
   b=98;
   for(k=3;k<=m;k++)
   {
      a=a*10+k-1;            /*a、b为m位最小与最大的P数*/
      b=b*10+10-k;
   }
   s=0;
   for(x=a;x<=b;x++)         /*枚举[a,b]中的每一个整数*/
   {
      w=x;
      for(k=0;k<=9;k++)
         f[k]=0;
      while(w>0)             /*分解x的各数字并分别累计*/
      {
         k=w%10;
         f[k]++;
         w=w/10;
      }
      for(t=0,k=0;k<=9;k++)
         if(f[k]>1)          /*测试是否有重复数字*/
         {
            t=1;
            break;
         }
      if(t==0)
      { 
         s++;                /*统计P数的个数*/
         if(n==s)            /*记录第n项*/
            z=x;
      }
   }
   if(s<n)
   {
      printf("n超过总项数!");
      return;
   }
   printf("%d位P序列共%ld项 \n",m,s);
   printf("序列第%d项为%ld \n",n,z);
}

(3)、程序运行示例及其注意事项:

请输入m,n:5,2017
5位P序列共27216项
序列第2017项为17023

以上枚举设计简单可行,但当m、n比较大时运行时间太长,有进一步改进与优化的必要;

2.程序的改进与优化:

(1)、优化设计要点;

1)、计算m位P数的个数;

显然m位P数的最高位可于1,2,……,9中取一个数字;

除最高位外其余的m-1为可于0~9中与最高位不同的其余9个数字中选m-1个,即为排列数A(9,m-1),存储在数组元素a[m-1]中;

除高二位外其余的m-2位可于0~9中与高二位不同的其余8个数字中选m-2个,即为排列数A(8,m-2),存储在数组元素a[m-2]中;

· · · · · ·

个位数字可于0~9中与高m-1位不同的11-m个数字中选1个,即为排列数A(11-m,1),存储在数组元素a[1]中;

显然m位P数的个数为9*a[m-1];

2)、由a数组元素确定P序列的第n项的各个数字;

根据n中含c=n/a[m-1]个a[m-1]确定最高位数字,n=n%a[m-1]为确定下一位数字做准备;然后根据n中含c=n/a[m-2]个a[m-2]确定次高位的数字,n=n%a[m-2]为确定下一位数字做准备;

一般地,根据n中含c=n/a[j]确定从高位开始的第j位数字:

  • c=n/a[j];n=n%a[j];(j=m-1,m-2,……,0)

  • c=(int)floor(n/a[j]);n=fmod(n,a[j])

为方便计算,引入a[0]=1;,选择尚未选取的第c个为最低位数字;

3)、设置b数组,为避免重复选取数字提供顺利;

首先b[i]=1;(i=0,1,……,9);

若已选取i作为某一位,则b[i]=0;,这样以后加b[j]和不增长,即在下一位数字选取时不可能选取i,避免了重复数字,这是P数的要求;

4)、特殊处理;

因最高位数字不能从0开始,故设置变量d:开始时d=1,以后均为d=0

(2)、程序设计:

#include<stdio.h>
#include<math.h>
int main()
{
   int c,d,i,j,k,m,s,b[10];
   double n,t,a[10];
   printf("请输入m,n:");
   scanf("%d,%lf",&m,&n);
   for(k=0;k<=9;k++)
      b[k]=1;
   for(t=1,j=1;j<=m-1;j++)
   {
      t=t*(10+j-m);
      a[j]=t;
   }
   printf("%d位P序列共%.0f项 \n",m,9*a[m-1]);
   if(n>9*a[m-1])
   {
      printf("n超过总项数!\n");
      return;
   }
   else
      printf("序列第%.0f项为 ",n);
   a[0]=1;
   d=1;
   for(j=m-1;j>=0;j--)
   {
      c=(int)floor(n/a[j])+d;
      n=fmod(n,a[j]);
      if(n==0 && j>0)
      {
         c--;
         n=a[j];
      }
      s=0;
      i=-1;
      if(j>0)
      {
         while(s<=c)
         {
            i++;
            s+=b[i];
         }
         printf("%d",i);
         b[i]=0;
         d=0;
      }
      else
      {
         while(s<c)
         {
            i++;
            s+=b[i];
         }
         printf("%d \n",i);
      }
   }
}

(3)、程序运行示例及其注意事项:

请输入m,n:10,1234567
10位P序列共3265920项
序列第1234567项为 4369702158

当m比较大时,应用枚举很难完成序列的统计与确定,而应用优化设计来求解则要简明快捷得多;

枚举设计可在较小范围内验证优化设计是否准确;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值