トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
Cマガ電脳クラブ(第056回) 加算連鎖
問題
数Aがある。Aの倍数を作りたいのだが、あいにく足し算しか使えない。
途中で作った数はそのあと使うことができるものとして、足し算の回数をできるだけ減らしたい。
例えば21倍を作るなら、
A+A=2A
A+2A=3A
2A+3A=5A
5A+5A=10A
A+10A=11A
10A+11A=21A
のように6回の足し算が必要である。ほかにもやり方はあるが、どうやっても5回以下ではできない。
さて、足し算10回まででは作れない最小の倍数は何倍だろうか。
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
//加算の回数の上限
const int LimitAddCnt = 10;
struct JyoutaiDef
{
internal int AddCnt;
internal List<string> AddLog;
internal HashSet<int> CreatedValSet;
}
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.AddCnt = 0;
WillPush.AddLog = new List<string>();
WillPush.CreatedValSet = new HashSet<int>() { 1 };
stk.Push(WillPush);
var UnionCreatedValSet = new HashSet<int>();
int CombiCnt = 0;
while (stk.Count > 0) {
JyoutaiDef Popped = stk.Pop();
if (Popped.AddCnt == LimitAddCnt) {
if (++CombiCnt % 1000000 == 1) {
Console.WriteLine("{0}個目の加算の組み合わせを発見。経過時間={1}",
CombiCnt, sw.Elapsed);
var sb = new System.Text.StringBuilder();
Popped.AddLog.ForEach(X => sb.AppendLine(X));
Console.WriteLine(sb.ToString());
}
UnionCreatedValSet.UnionWith(Popped.CreatedValSet);
continue;
}
//Push処理
Action<int, int, int> PushSyori = (pWK1, pWK2, pSum) =>
{
WillPush.AddCnt = Popped.AddCnt + 1;
string WkLog = string.Format("{0}+{1}={2}", pWK1, pWK2, pSum);
WillPush.AddLog = new List<string>(Popped.AddLog) { WkLog };
WillPush.CreatedValSet = new HashSet<int>(Popped.CreatedValSet) { pSum };
stk.Push(WillPush);
};
int[] wkArr = Popped.CreatedValSet.ToArray();
int MaxVal = wkArr.Max();
var wkCreatedValSet = new HashSet<int>(); //作成した和
for (int I = 0; I <= wkArr.GetUpperBound(0); I++) {
for (int J = I; J <= wkArr.GetUpperBound(0); J++) {
int wkSum = wkArr[I] + wkArr[J];
if (wkSum <= MaxVal) continue; //昇順に和を作成していく経路とする
if (wkCreatedValSet.Add(wkSum) == false) continue;
PushSyori(wkArr[I], wkArr[J], wkSum);
}
}
}
for (int I = 1; I <= 1024; I++) { //2の10乗 = 1024
if (UnionCreatedValSet.Contains(I)) continue;
Console.WriteLine("解は{0}。経過時間={1}", I, sw.Elapsed);
break;
}
}
}
実行結果
省略
9000001個目の加算の組み合わせを発見。経過時間=00:01:14.2006545
1+1=2
1+2=3
2+3=5
2+5=7
7+7=14
7+14=21
3+21=24
2+24=26
5+26=31
24+24=48
10000001個目の加算の組み合わせを発見。経過時間=00:01:22.4590421
1+1=2
1+2=3
1+3=4
4+4=8
1+8=9
9+9=18
4+18=22
3+22=25
18+25=43
1+43=44
解は191。経過時間=00:01:30.0534288
解説
深さ優先探索で発見した作成可能な和を、HashSetジェネリックで管理してます。