1. 程式人生 > >Self-Numbers解題報告

Self-Numbers解題報告

題目描述:


在1949年印度數學家D. R. Daprekar發現了一類稱作Self-Numbers的數。對於每一個正整數n,我們定義d(n)為n加上它每一位數字的和。例如,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。如前13個Self-Number為1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97。我們將第i個Self-Number表示為a[i],所以a[1]=1, a[2]=3, a[3]=5. . . 


輸入


輸入包含整數N、K、s1. . . sk,其中1<=N<=10^7,1<=K<=5000,以空格和換行分割。


輸出


在第一行你需要輸出一個數,這個數表示在閉區間[1, N]中Self-Number的數量。第二行必須包含以空格劃分的K個數,表示a[s1]. . a[sk],這裡保證所有的a[s1]. . a[sk]都小於N。(例如,如果N=100,sk可以為1-13,但不能為14,因為a[14]=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


資料範圍限制


1<=N<=10^7,1<=K<=5000

解題報告:

一來看這道題,認為是一道簡單的模擬題,每個數字列舉比它小的所有數,看它是否是self-number,那麼,時間複雜度是O(n^2),一看n是10^7就知道這樣死定了,於是,我想了一下,例如我檢測999999是否是self-number,還需要從1,2,3,4,5……甚至999這些對於999999很小的數開始列舉嗎,顯然不需要,因為n最大是10^7,所以一個數字所有位加起來最大的是999999(為54),所以,最多從x-54開始列舉就是了,程式碼如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cctype>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
using namespace std;
int s[5005],n,k,num,a[10000005];
inline void scan(int &v)//讀入優化 
{
v=0;char c=0;int p=1;
while(c<'0'||c>'9'){if(c=='-')p=-1;c=getchar();}
while(c>='0'&& c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
v*=p;
}
int d(int x)
{
int ans=x;
while(x)
ans+=x%10,x/=10;
return ans;
}
bool check(int x)//檢測是否是Self-Number 
{
bool flag=true;
for(int i=x>54?x-54:0;i<=x;i++)
if(d(i)==x)flag=false;
return flag;
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)scan(s[i]);
for(int i=1;i<=n;i++)
if(check(i))
num++,a[num]=i;
cout<<num<<endl;
for(int i=1;i<=k;i++)
printf("%d ",a[s[i]]);
return 0;
}

本來還是比較自信的,其實提交只有60分(超時),於是只有想一想有沒有更好的演算法了,於是想到了篩法,這樣就不需要check函數了,查它是不是隻需要O(1)而不是O(54)。提高了效率

程式碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cctype>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
using namespace std;
int s[5005],n,k,num,a[10000005];
bool flag[10000005]; 
inline void scan(int &v)//讀入優化 
{
v=0;char c=0;int p=1;
while(c<'0'||c>'9'){if(c=='-')p=-1;c=getchar();}
while(c>='0'&& c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
v*=p;
}
void d(int x)
{
int ans=x;
while(x)
ans+=x%10,x/=10;
flag[ans]=true;
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)scan(s[i]);
for(int i=1;i<=n;i++)
{
if(!flag[i])//沒被篩 
num++,a[num]=i;
d(i);//篩 
}
cout<<num<<endl;
for(int i=1;i<=k;i++)
printf("%d ",a[s[i]]);
return 0;
}

語種:C++