SGU 108 Self-numbers II

题意:定义d(i)=i+i的各个位上数字之和。一个数如果不能够通过函数d来得到,则称之为self-number

给出n(n<=10^7)与k个数a[1]……a[k],求不超过n的self-number个数。并依次输出第a[1]……a[k]个self-number


此题的小技巧甚多。

首先,由于这里的内存限制为4096KB,如果直接打表筛选的话,需要开设数组大小为10^7,bool型的话,每个占1B,总的内存为10^7B/1024,约为9700+KB。

因此必须进行优化。用位压缩或者滚动数组都可以节省内存。


使用滚动数组的话,需考虑以多少为循环。

考虑d(i)是通过i来得到,i最大位数为7,i的各个位之和不会超过7*9=63,即d(i)-i<=63,由i得到的下一个数d(i)不会超过i+63,也就是说,i的前一个数最远到i-63。因此只需记录64个位置的值,每满64向后滚动一位,设置标记数组f大小为64.。


另外该题还需注意的是:a[1]……a[k]并不是按大小顺序给出,并且可能存在大小相等的数。因此需先对a进行排序。


对于输出:

这题打表可以发现10^7以内的self-number不超过10^6个,开设int型数组来保存结果的话,10^6大约需要4*10^6/1024,约为3900+KB,还是勉强可以过掉的。但既然用了优化就优化彻底一点吧:用结构体,成员一个是a[i]的值num,一个是a[i]的输入顺序id,再一个ans用来保存结果,即空间为3*5000*sizeof(int)B。 记录结果之后,再按照id排序一下,顺序输出ans即可。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int id,num,ans;
}a[5005];
bool cmp(node x,node y) {return x.num<y.num;}
bool cmp2(node x,node y) {return x.id<y.id;}
bool f[64];
int main()
{
    int i,j,n,k;
    scanf("%d%d",&n,&k);
    for(i=1;i<=k;++i) {scanf("%d",&a[i].num);a[i].id=i;}
    sort(a+1,a+1+k,cmp);
    memset(f,0,sizeof(f));
    int ans=0,t=1;
    for(i=1;i<=n;++i)
    {
        if(!f[i%64])      //(听说如果用位运算代替取模运算会更加高效)
        {
            ++ans;
            while(a[t].num==ans&&t<=k)    //可能有重复的数
                a[t++].ans=i;
        }
        f[i%64]=0;
        int x=i,d=0;
        while(x)
        {
            d=d+x%10;
            x/=10;
        }
        f[(i+d)%64]=1;
    }
    sort(a+1,a+1+k,cmp2);
    printf("%d\n",ans);
    for(i=1;i<k;++i) printf("%d ",a[i].ans);
    printf("%d\n",a[i].ans);
    return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值