「プロジェクトオイラー問74」の編集履歴(バックアップ)一覧に戻る

プロジェクトオイラー問74 - (2014/03/02 (日) 06:00:25) のソース

http://projecteuler.net/problem=74

*Problem 74 「桁の階乗による連鎖」 †
145は各桁の階乗の和が145と自分自身に一致することで有名である.

1! + 4! + 5! = 1 + 24 + 120 = 145

169の性質はあまり知られていない. これは169に戻る数の中で最長の列を成す. このように他の数を経て自分自身に戻るループは3つしか存在しない.

169 → 363601 → 1454 → 169
871 → 45361 → 871
872 → 45362 → 872

どのような数からスタートしてもループに入ることが示せる.

例を見てみよう.

69 → 363600 → 1454 → 169 → 363601 (→ 1454)
78 → 45360 → 871 → 45361 (→ 871)
540 → 145 (→ 145)

69から始めた場合, 列は5つの循環しない項を持つ. また100万未満の数から始めた場合最長の循環しない項は60個であることが知られている.

100万未満の数から開始する列の中で, 60個の循環しない項を持つものはいくつあるか?

解法
7ケタが全部9でも254万程度で、8桁になっても280万程度です。
つまり多めに見積もっても260万程度までをメモ化しておけばよいと分かります。
これで十分間に合います。
今回もC++でPrologではありません。


 #include<stdio.h>
 #include<string.h>
 
 char lens[2600000];
 const int fact[10]={1,1,2,6,24,120,720,5040,40320,362880};
 
 int toNextNum(int num){
 	int re=0;
 	while(num!=0){
 		re+=fact[num%10];
 		num/=10;
 	}
 	return re;
 }
  
 int search(int num){
 	if(num==toNextNum(num)){
 		lens[num]=0;
 		return 0;
  	}
 	if(lens[num]>=0){
 		return lens[num];
 	}else{
  		lens[num]=0;
 		lens[num]=search(toNextNum(num))+1;
 		if(lens[num]>60)lens[num]=60;
 		return lens[num];
 	}
 } 
 
 int main(){
 	int ans=0;
 	memset(lens,-1,sizeof(lens));
 	for(int i=1;i<1000000;i++){
  		if(lens[i]>=0){
 			ans+=(lens[i]>=60);
 		}else{
 			ans+=(search(i)>=60);
 		}
 	}
 	printf("%d\n",ans);
 }