Olympiad(求区间内的美丽数)超详细 (C,C++)

题目:

  You are one of the competitors of the Olympiad in numbers. The problem of this year relates to beatiful numbers. One integer is called beautiful if and only if all of its digitals are different (i.e. 12345 is beautiful, 11 is not beautiful and 100 is not beautiful). Every time you are asked to count how many beautiful numbers there are in the interval [a,b]  (a≤b). Please be fast to get the gold medal!

翻译:

  你是奥运会的竞争对手之一。今年的问题与令人愉足的数字有关。一个整数被称为美丽的原因只有当其所有位上的数字是不同的时候(即12345是美丽的,11不漂亮,100不漂亮)。每次案例,你被要求计算有多少美丽的数字在间隔‎‎[a, b] 之间(a ≤‎ b)‎。请快点拿金牌!‎

Input:

The first line of the input is a single integer T (T≤1000), indicating the number of testcases.

For each test case, there are two numbers aa and bb, as described in the statement. It is guaranteed that 1≤a≤b≤100000.

‎输入的第一行是单个整数‎‎T (T‎‎≤‎ 1000)‎,指示测试案例的数量。‎
‎对于每个测试案例,有两‎个数字‎‎a和b‎,如声明中所述。这是保证1≤ab≤100000.

Output:

For each testcase, print one line indicating the answer.

对于每个测试案例,打印出一行答案。‎

Sample Input:

2
1 10
1 1000

Sample Output:

10
738

思考过程:

  当我们看完题目时,能很清晰的知道他是要求计算出两个数之间的:满足某种条件的数字,看到这种类型的题我们应当能立刻的想到这个题能够用的前缀和的方法。

  当然,你用暴力算法也完全可以解决这个问题,但...题目接不接受就不一定了n _ n

  我们的目的是为了AC,可不要盲目的暴力求解哦,花费大把时间结果报了个Time Limit Exceeded。


  用前缀和的方法,我们首先要造出一个前缀和数组,即储存着从一到这个下标数字之间一共有多少个美丽数的数组,我们把它命名为sum,而由题可知我们输入的数最大为100000,为了防止出现数据溢出,这里我们把它的长度定为100010。

#include<stdio.h>
int sum[100010];

  我们经过良好的九年义务语文教育,可以得知满足美丽数条件为:这个数所有位上的数字都是不同的,即每一位上的数字不能重复

  在这里我们来了解两种求出满足美丽数条件的数组的方法:

第一种:暴力求解

  这个是最容易理解,也是最暴力的方法,就是把从一到十万的数字每个都拆一遍。

  首先,我们要用以下代码判断他是几位数,这里要注意的是要用从第二个往后要用else if,而不是再另外写一个if,就是让我们下面的判断,是在前面的判断不成立时才开始执行。

if(i/10000!=0)
else if(i/1000!=0)
else if(i/100!=0)
else if(i/10!=0)
else if(i/1!=0)

  然后,再灵活的运用用除以 '/' 和取模 '%' 这两个运算符把数字的每一位都分离出来。以五位数为例,代码实现如下:

int A=i/10000,B=i%10000/1000,C=i%1000/100,D=i%100/10,E=i%10/1; 

  当分离完每个位上的数字后,我们就需要判断一下他们是否相等,即是否美丽数的条件:

  (这里我们说一下代码的书写,当判断条件很多时,我们可以在逻辑关系符左右加上空格,来方便我们后面的检查,和别人的阅读。)

 if(A!=B && A!=C && A!=D && A!=E && B!=C && B!=D && B!=E && C!=D && C!=E && D!=E)

  最后,如果该数字 i 满足条件,那么只需要让 sum[i] 等于 sum[i-1] 加一就行了,即当范围扩大后又有一个数字满足了美丽数的条件,所以该范围内的美丽数个数 等于 到这个范围之前的那个美丽数的个数 加一。如果不满足条件了,就说明该范围内的美丽数个数,和范围没有扩大前一样。翻译为c语言就是:


if(A!=B&& A!=C&& A!=D&& A!=E&& B!=C&& B!=D&& B!=E&& C!=D&& C!=E&& D!=E )
sum[i]=sum[i-1]+1;
else sum[i]=sum[i-1];

这种方法求前缀和数组的整体代码如下:

for(int i=1;i<=100000;i++)
	{
		if(i/10000!=0)
		{
			int A=i/10000,B=i%10000/1000,C=i%1000/100,D=i%100/10,E=i%10/1; 
		    if(A!=B&& A!=C&& A!=D&& A!=E&& B!=C&& B!=D&& B!=E&& C!=D&& C!=E&& D!=E )
		    sum[i]=sum[i-1]+1;
		    else sum[i]=sum[i-1];
		}
		else if(i/1000!=0)
		{
			int A=i/1000,B=i%1000/100,C=i%100/10,D=i%10/1; 
		    if(A!=B&& A!=C&& A!=D&& B!=C&& B!=D&& C!=D)
		    sum[i]=sum[i-1]+1;
		    else sum[i]=sum[i-1];
		}
		else if(i/100!=0)
		{
			int A=i/100,B=i%100/10,C=i%10/1; 
		    if(A!=B&& A!=C && B!=C)
		    sum[i]=sum[i-1]+1;
		    else sum[i]=sum[i-1];
		}
		else if(i/10!=0)
		{
			int A=i/10,B=i%10/1; 
		    if(A!=B)
		    sum[i]=sum[i-1]+1;
		    else sum[i]=sum[i-1];
		}
		else if(i/1!=0)
		    sum[i]=sum[i-1]+1;
	}

第二种:用数组来存某个数字出现的次数

  用一个长度为十的数组,存下来一个数里出现了几次某个数字。

  由于题上用过了a,b,所以我们把该数组命名为d。

  举个栗子:用d[0]存下来这个数里的0出现了几次, 用d[1]存下来1在这个数里出现了几次。

  这里也介绍另外一种把数的每个位上的数字分离出来的方法。

  循环法求每一位数,在每一次的循环里把一个数位(往往是最后一位)上的数字赋给c,每次循环的  最后都会删掉数的最后一位数字(因为已经用过了),一直到把数删到为0为止。这样也免去了判断数是几位数的过程。

int c;
while(i>0)
{
    c=i%10;

    ..... 

    i/=10;
}

  在这个的循环中,就需要我们来一次次用a数组存下某个数出现了几次。代码实现为:

while(x>0)
{
   c=x%10;
   d[c]++;
   x/=10;
}

  最后,再循环一下d数组,看一下有没有哪个数字出现了两次及以上,如果出现的话就做个标记,让前缀和数组加一,如果没有了就让这个数的前缀和等于上个数的前缀和。在这里我们定义一个用来做标记的变量e,如果有某个数字出现的次数大于1了就让e++。

int e=0;
for(j=0;j<10;j++)
   if(d[j]>=2)
      e++;

if(e==0)
    sum[i]=sum[i-1]+1;
else
    sum[i]=sum[i-1];

整体代码如下

#include<stdio.h>
int p[110000],i,j;

int main()
{
	for(i=1;i<=100000;i++)
    {
        int wms=1;
        int a[10]={0};
        int c;
        int x=i;
        while(x>0)
        {
            c=x%10;
            a[c]++;
            x/=10;
        }
        for(j=0;j<10;j++)
        {
            if(a[j]>=2)
            {
                wms=0;
                break;
            }
        }
        if(wms==0)
            p[i]=p[i-1];
        else
            p[i]=p[i-1]+1;
        
    }
    int T,a,b;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&a,&b);
        printf("%d\n",p[b]-p[a-1]);

    }
}

输入输出

  得到了前缀和数组,输入输出就很简单啦。

  我们首先要输入有几次案例,再输入每次案例的范围,输出范围两端的前缀和之差即范围内的美丽数个数。我相信大多数同学都会用代码实现这句话的,呕吼!!!

int T,a,b;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&a,&b);
		printf("%d\n",sum[b]-sum[a-1]);
	 } 

  到这里我们的这道题就结束啦。希望本文对老爷们有用,祝老爷们早日AC哦!

  如果对老爷们有帮助的话,就助力一下阿皓的梦想吧,点个赞或者长按大拇指或者小心心一键三连吧n _ n,谢谢读者老爷们啦。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
这个问需要使用Python中的Bokeh库来设计交互式图表。首先,我们需要导入Bokeh库和据集,并创建一个Figure对象来设置坐标轴和图表大小: ```python from bokeh.plotting import figure, show from bokeh.models import ColumnDataSource, HoverTool import pandas as pd # 导入据集 df = pd.read_csv('olympic_data.csv') # 创建ColumnDataSource对象 source = ColumnDataSource(data=dict( x=df['Medal_Count'], y=df['Athlete_Count'], sport=df['Sport'], medal_type=df['Medal'], athlete_name=df['Name'], nationality=df['Nationality'], gender=df['Sex'] )) # 创建Figure对象 p = figure(title="2016 Rio Olympics Medal Count vs Athlete Count", x_axis_label='Medal Count', y_axis_label='Athlete Count', plot_width=800, plot_height=600) ``` 接下来,我们需要使用scatter()方法将据点绘制到图表上,并设置颜色和大小,以及添加悬停工具: ```python # 绘制散点图 p.scatter('x', 'y', source=source, size=10, alpha=0.8, color='medal_type', legend_group='sport') # 添加悬停工具 hover = HoverTool(tooltips=[ ('Athlete Name', '@athlete_name'), ('Sport', '@sport'), ('Nationality', '@nationality'), ('Gender', '@gender'), ('Medal Type', '@medal_type') ]) p.add_tools(hover) # 设置图例位置 p.legend.location = "top_left" # 显示图表 show(p) ``` 这样,我们就可以得到一个交互式的散点图,可以通过滑动控件来筛选所显示的大项,悬停于据点上时可以查看更多信息。 需要注意的是,上述代码中的据集需要根据实际情况进行调整和更改,以适应所需的据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值