sgu 108 self number(数学问题滚动数组)

来源:互联网 发布:如何加强网络安全管理 编辑:程序博客网 时间:2024/04/30 06:24

题意:首先引入d()函数,d(n)=n+n的各位数字之和。比如d(75) = 75 + 7 + 5 = 87。一个数被称作self number当且仅当对所有的i<n,d(i) != n。(即n不存在generator)。

输入: N, K, s1...sk. (1<=N<=10^7, 1<=K<=5000) 。要求输出1~N的self number数量。然后依次输出第s1~第sk个self number(输入确保第sk个self number小于N)

思路:拿起来就模拟,像筛质数那样,开了一个10^7大小的flag数组,结果TLE。参考了别人的代码(http://www.cnblogs.com/Rinyo/archive/2012/12/04/2802337.html)发现可以用滚动数组。滚动数组以前我只在dp问题里见过,这还是第一次用在数学问题中,还是要灵活!!

滚动周期64是这样的来的:因为数不超过10^7,所以所有数的最大各位和为63(来自9999999),所以周期取64。这样做的依据是n < d(n) <= n+63

期间wa了两次是因为题目中的s1~sk并不是按照递增顺序给出的,所以必须另外加一些标志位来进行处理。

还知道了一个小技巧,对于此题求某个数各个位上的数之和,可以提前预处理好0~10000的各位之和d[i],对于任意数k,把这个数拆成两部分后分别求和,再相加即可。所以,d[k]=d[k/10000]+d[k%10000]

#include <stdio.h>#include <string.h>#include <stdlib.h>#define N 64int flag[N];int d[10001];struct node{int index,id,res;//index为输入的下标,id为输入的Sk,res保存值}p[5005];int num=0;int digit(int x){int res=0;while(x){res += x%10;x /= 10;}return res;}void init(){//打表列出前10000个数的digit和int i;for(i = 1;i<=10000;i++)d[i] = digit(i);}int next(int x){//求d(x)int res = x;res += d[x%10000];res += d[x/10000];return res;}int cmp1(const struct node *a,const struct node *b){//用于筛的过程中保存return (*a).id-(*b).id;}int cmp2(const struct node *a,const struct node *b){//用于还原成输入的顺序return (*a).index-(*b).index;}int main(){int res = 0,i,n,m,now = 1;freopen("a.txt","r",stdin);init();memset(flag,0,sizeof(flag));scanf("%d %d",&n,&m);for(i = 1;i<=m;i++){scanf("%d",&p[i].id);p[i].index = i;}qsort(p+1,m,sizeof(struct node),cmp1);for(i = 1;i<=n;i++){if(!flag[i%N]){//表示找到self numbernum++;while(now<=m && num == p[now].id)//万一输入的s1...k中还有相同的呢p[now++].res = i;}flag[i%N] = 0;//筛完这个位置立即归零flag[next(i)%64] = 1;}qsort(p+1,m,sizeof(struct node),cmp2);printf("%d\n",num);for(i = 1;i<=m;i++)printf("%d ",p[i].res);putchar('\n');return 0;}


0 0
原创粉丝点击