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

题意:首先引入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 64
int 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 number
			num++;
			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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值