SGU 108 Self-numbers II 翻译 题解

108. Self-numbers 2

每个测试点时间限制: 2.50 sec. 
内存限制: 4096 KB

 

在1949年印度的数学假D.R. Kaprekar发现了一种叫做self-number的经典数字,对于任意正整数n,定义d(n)为n加上n的各个位上的数字(d是数字的意思,Kaprekar发明的一个术语)。如:d(75) = 75 + 7 + 5 = 87。给定任意正整数n,你可以构建出无限的整数递增:n, d(n), d(d(n)), d(d(d(n))), ……举个例子,你从33开始,那么下一个数就是33 + 3 + 3 = 39, 再下一个就是39 + 3 + 9 = 51, 接着就是 51 + 5 + 1 = 57, 那样就生成了一个序列: 33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ... 这里n叫做d(n)母数 上面的数列中,33是39的母数,39是51的母数,51是57的母数,以此类推……有些数字不止一个母数,比如101有两个母数,91和100。没有母数的数字就叫做self-number。让a[i]成为第i个self-number。现在存在13个小于100的self-number: 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 和 97. (第一个 self-number是a[1]=1, 第二个是 a[2] = 3, :, 第十三个是 a[13]=97);

 

输入:

包含整数 N, K, s1...sk. (1<=N<=107, 1<=K<=5000) 被空格和换行分割开。

 

输出:

第一行你必须输出一个数字——表示在[1,n]中self-numbers的个数。第二行必须输出K个数字:a[s1]..a[sk],用空格分开。保证所有的在a[s1]..a[sk]的self-numbers都在[1,n]的区间内,(比如N = 100, sk 就只能等于1..13并且不能等于14, 以为第14个self-number a[14] = 108, 108 > 100)

 

样例输入

100 10
1 2 3 4 5 6 7 11 12 13

 

样例输出

13
1 3 5 7 9 20 31 75 86 97

================================华丽的分割线 ================================

  很清晰吧,不是思路清晰,而是时间不是问题,允许暴搜,但是问题是空间!
  在网上学了一个很厉害的招数,压缩空间,因为最大是107,1<=d(i)-i<=7*9,在这个范围之内,那就用一个hash表来储存每个点是否是self-number,如果是就为1不是就为0,每个初始都是1,然后慢慢的变成0,空间不允许那么大,就开一个63的数组,然后暴搜就是。

  借鉴了http://hi.baidu.com/zhh_climber/blog/item/37bee28852aefd97a5c272f4.html的思路,不过没想通为什么要开0~63,我觉得0~62就可以了。
思路: 

  从1循环到N,设循环到了i,则最多影响i+63,再之后的就影响不到,依然是1,之前的已经循环完了。大概就是这么一个思路。

 
  
#include < stdio.h >
struct self{
int id, num, ans;
}num[
5000 ];
int arr[ 10000 ];
int self[ 63 ];
#define swap(i, j)do{\
struct self t;\
t
= i;\
i
= j;\
j
= t;\
}
while ( 0 )

void qsort( int start, int end)
{
int i = start - 1 , j = end;
if (start >= end){
return ;
}
while (i < j){
while (i < j && num[ ++ i].num < num[end].num){
continue ;
}
while (i < j && num[ -- j].num > num[end].num){
continue ;
}
if (i < j){
swap(num[i], num[j]);
}
}
if (num[i].num > num[end].num){
swap(num[i], num[end]);
}
qsort(start, i
- 1 );
qsort(i
+ 1 , end);
}
int len;
int now;
int ans[ 2000 ];

int main( void )
{
int i;
int m, n;
int next;
scanf(
" %d%d " , & n, & m);
for (i = 0 ; i < m; i ++ ){
scanf(
" %d " , & num[i].num);
num[i].id
= i;
}
qsort(
0 , m - 1 );
for (i = 0 ; i < 10000 ; i ++ ){
arr[i]
= arr[i / 10 ] + i % 10 ;
}
for (i = 0 ; i < 63 ; i ++ ){
self[i]
= 1 ;
}
for (i = 1 ; i <= n; i ++ ){
/* 检查了好久!! 忘记求模了 */
if (self[i % 63 ]){
len
++ ;
while (num[now].num == len){
num[now
++ ].ans = i;
}
}
// 设置i, 这是一个hash(滚动数组)
self[i % 63 ] = 1 ;
// 计算d(i)
next = i + arr[i / 10000 ] + arr[i % 10000 ];
// d(i)不是self-num
self[next % 63 ] = 0 ;
}
printf(
" %d\n " , len);
for (i = 0 ; i < m; i ++ ){
ans[num[i].id]
= num[i].ans;
}
for (i = 0 ; i < m; i ++ ){
if (i != 0 ){
printf(
" " );
}
printf(
" %d " , ans[i]);
}
printf(
" \n " );
return 0 ;
}

转载于:https://www.cnblogs.com/yylogo/archive/2011/06/09/SGU-108.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值