#P13787. [NOIP2021] 报数

目录

一,题目

二,题意分析

三,做法

1.直接模拟题意

  2,用筛法来解

3,正解(加上记忆化)


一,题目


二,题意分析

题目意思是说:每次输入一个数,然后先判断这个数是否为某一个十进制中包含7的数的倍数,如果是则输出-1,否则找到该数后的第一个不为任何一个十进制中包含7的数的倍数的数,输出即可。


三,做法


1.直接模拟题意

首先我们程序中必须用一个函数p计算出x的十进制是否中包含7(包含就返回0,否则返回1)

bool p(int x)
{
  while(x)
  {
    if(x % 10 == 7) return 0;
    x /= 10;
  }
  return 1;
}

然后还要一个函数f计算出x是否为某一个十进制中包含7的数的倍数(是就返回0,否则返回1)

bool f(int x)
{
  for(int i = 1; i <= x / i; i++)
    if(x % i == 0 && (p(i) == 0 || p(x / i) == 0))
//其实也就是枚举x的约数,看看约数中有没有十进制中包含7的数
      return 0;
  return 1;
}

然后就是主函数。每次输入x,然后判断f(x)是否等于0(判断对方报的数报对了没),是则输出-1(对方没报对),否则从x + 1开始枚举i(枚举你的要报的数),每次都要判断f(i)是否等于1(判断i是否可以报出来),等于1则输出i(可以报),break,否则i继续++

signed main()
{
  scanf("%lld",&t);
  while(t--)
  {
    scanf("%lld",&x);
    if(f(x) == 0) printf("-1\n");
    else
    {
      int i = x + 1;
      while(1)
      {
        if(f(i) == 1)
        {
          printf("%lld\n",i);
          break;
        }
        i++;
      }
    }
  }
  return 0;
}

总体程序:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t,x,l[10000001];
bool p(int x)
{
  while(x)
  {
    if(x % 10 == 7) return 0;
    x /= 10;
  }
  return 1;
}
bool f(int x)
{
  for(int i = 1; i <= x / i; i++)
    if(x % i == 0 && (p(i) == 0 || p(x / i) == 0))
      return 0;
  return 1;
}
signed main()
{
  scanf("%lld",&t);
  while(t--)
  {
    scanf("%lld",&x);
    if(f(x) == 0) printf("-1\n");
    else
    {
      int i = x + 1;
      while(1)
      {
        if(f(i) == 1)
        {
          printf("%lld\n",i);
          break;
        }
        i++;
      }
    }
  }
  return 0;
}


  2,用筛法来解

 我们可以发现,这道题和埃氏筛法的思想很像,其实就是模拟,我们可以用一个vis数组来记录一个数是否应该被跳过。

初始化vis的代码:

void fin()
{
  for(int i = 1; i < 10000001; i++)
    if(vis[i] == 0 && p(i) == 1)
      for(int j = 1; i * j < 10000011; j++)
        vis[i * j] = 1;
}


这样我们就预处理好了10^7以内的数是否应该被跳过

于是乎,我又交了一发这样的代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t,x,vis[10000011];
bool p(int x)
{
  while(x)
  {
    if(x % 10 == 7) return 1;
    x /= 10;
  }
  return 0;
}
void fin()
{
  for(int i = 1; i < 10000001; i++)
    if(vis[i] == 0 && p(i) == 1)
      for(int j = 1; i * j < 10000011; j++)
        vis[i * j] = 1;
}
signed main()
{
  fin();
  scanf("%lld",&t);
  while(t--)
  {
    scanf("%lld",&x);
    if(x < 0)
    {
      printf("1");
      continue;
	}
    if(vis[x] == 1) printf("-1\n");
    else
    {
      for(int i = x + 1;i;i++)
      {
        if(vis[i] == 0)
        {
          printf("%lld\n",i);
          break;
        }
      }
    }
  }
  return 0;
}

你以为这样就结束了吗?

非也,非也。

 我又荣幸地拿到了70分,T了三个点。


3,正解(加上记忆化)

我看了看样例,突然发现样例中多次询问了同一个数,就用l数组来记录已经询问过的数,并在每次询问时都判断一下l[x]是否为0,不为0则直接输出l[x],否则进行计算,最后再把结果记录到l[x]中。

没想到真的AC了。。。

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t,x,l[10000011],vis[10000011];
bool p(int x)
{
  while(x)
  {
    if(x % 10 == 7) return 1;
    x /= 10;
  }
  return 0;
}
void fin()
{
  for(int i = 1; i < 10000001; i++)
    if(vis[i] == 0 && p(i) == 1)
      for(int j = 1; i * j < 10000011; j++)
        vis[i * j] = 1;
}
signed main()
{
  fin();
  scanf("%lld",&t);
  while(t--)
  {
    scanf("%lld",&x);
    if(x < 0)
    {
      printf("1");
      continue;
	}
    if(l[x] != 0)
    {
      printf("%lld\n",l[x]);
      continue;
    }
    if(vis[x] == 1) printf("-1\n");
    else
    {
      for(int i = x + 1;i;i++)
      {
        if(vis[i] == 0)
        {
          l[x] = i;
          printf("%lld\n",i);
          break;
        }
      }
    }
  }
  return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值