#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;
}

### NOIP1999 普及组 回文数 问题 解题思路 对于NOIP1999普及组中的回文数问题,核心挑战在于处理大整数以及不同进制下的运算。由于给定的数值可能达到一百位以内,在常规数据类型的表示范围之外,因此需要采用特殊的数据结构如数组或字符串来存储这些大数[^1]。 #### 数据存储方式的选择 考虑到操作便利性和效率,使用数组而非字符转换可以简化编码过程,避免频繁地处理ASCII码偏移量等问题。通过将每一位数字单独保存到数组的一个元素中,能够更直观有效地执行后续计算逻辑[^4]。 #### 进制转换与加法运算 当涉及到非十进制情况时,需特别关注如何正确实施基于指定基数(N)的算术运算。每当两个相同位置上的值相加大于等于当前基底,则应向更高一位借位并调整余下部分;此过程中还需留意边界条件以防止溢出错误发生[^3]。 #### 判断是否构成回文序列 完成一次迭代后的求和结果应当被重新评估其正反顺序排列的一致性——即验证所得新串是否满足回文特性。如果未能形成期望模式,则重复上述步骤直至获得满意解答或是超出预设的最大尝试次数为止[^2]。 ```cpp #include <iostream> using namespace std; bool isPalindrome(int *num, int length){ for (int i=0;i<length/2;i++){ if(num[i]!=num[length-i-1]) return false; } return true; } void addOneToNum(int base,int num[],int &len){ bool carry=true; for(int i=len-1;(i>=0)&&(carry==true);i--){ num[i]+=1; if(num[i]>=base){ num[i]-=base; carry=(i>0); }else{ carry=false; } } if(carry) { //最高位有进位 len++; for(int j=len;j>0;j--) { num[j]=num[j-1]; } num[0]=1; } } // 主函数框架示意 /* int main(){ int N,M,maxTry=30; cin>>N>>M; // 初始化变量... } */ ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值