トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
Cマガ電脳クラブ(第059回) お散歩仲間
問題
15人の女生徒がいる。彼女たちはお散歩仲間で、毎日3人ずつ5組に分かれて散歩をしている。
1週間(7日間)で、各人がほかのすべての14人と1回ずついっしょに散歩をするようにしたい。
1週間分の組み合わせをどのようにすればよいだろうか。
組み合わせは、1通り求めるだけでかまわない。
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
struct JyoutaiDef
{
internal int CurrP;
internal int[] HitoHaitiArr;
internal List<int[]> HitoHaitiArrList;
}
const int UB = 14;
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
int[] AllHitoArr = Enumerable.Range(1, 15).ToArray();
WillPush.HitoHaitiArrList = new List<int[]>();
//最初の組み合わせは1,2,3,4,5,6,7,8,9,10,11,12,13,14,15で固定とする
WillPush.HitoHaitiArrList.Add(AllHitoArr);
WillPush.HitoHaitiArr = new int[15];
WillPush.CurrP = 0;
Stk.Push(WillPush);
int PoppedCnt = 0;
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
if (++PoppedCnt % 100000 == 1)
Console.WriteLine("{0}回目のPop処理を実行中。経過時間={1}", PoppedCnt, sw.Elapsed);
if (Popped.CurrP > UB) {
Popped.HitoHaitiArrList = new List<int[]>(Popped.HitoHaitiArrList);
Popped.HitoHaitiArrList.Add((int[])Popped.HitoHaitiArr.Clone());
Array.Clear(Popped.HitoHaitiArr, 0, 15);
Popped.CurrP = 0;
}
if (Popped.HitoHaitiArrList.Count == 7) {
Console.WriteLine("解を発見。経過時間={0}", sw.Elapsed);
PrintAnswer(Popped.HitoHaitiArrList);
return;
}
int[] NonUseHitoArr = AllHitoArr.Except(Popped.HitoHaitiArr).ToArray();
var WillUseHitoList = new List<int>();
//Push処理
Action<int, int, int> PushSyori = (pWillUseHito1, pWillUseHito2, pWillUseHito3) =>
{
WillPush.CurrP = Popped.CurrP + 3;
WillPush.HitoHaitiArr = (int[])Popped.HitoHaitiArr.Clone();
WillPush.HitoHaitiArr[Popped.CurrP] = pWillUseHito1;
WillPush.HitoHaitiArr[Popped.CurrP + 1] = pWillUseHito2;
WillPush.HitoHaitiArr[Popped.CurrP + 2] = pWillUseHito3;
WillPush.HitoHaitiArrList = Popped.HitoHaitiArrList;
Stk.Push(WillPush);
};
int WillUseHito1 = NonUseHitoArr.Min(); //トリオの最初は、未使用の最小番号
//トリオ内では昇順に配置
int[] KouhoHitoArr = NonUseHitoArr.Where(X => X > WillUseHito1).ToArray();
int KouhoHitoMax = KouhoHitoArr.Max();
foreach (int EachHito2 in KouhoHitoArr) {
//4は、2日目の1番目のトリオとする
if (Popped.HitoHaitiArrList.Count == 1 && Popped.CurrP == 0)
if (EachHito2 != 4) continue;
//5は、3日目の1番目のトリオとする
if (Popped.HitoHaitiArrList.Count == 2 && Popped.CurrP == 0)
if (EachHito2 != 5) continue;
//6は、4日目の1番目のトリオとする
if (Popped.HitoHaitiArrList.Count == 3 && Popped.CurrP == 0)
if (EachHito2 != 6) continue;
if (EachHito2 <= WillUseHito1) continue; //昇順に配置
//昇順なので2人目に最大値は配置できない
if (EachHito2 == KouhoHitoMax) continue;
//過去に同じトリオでないこと
if (HasSameTrio(Popped.HitoHaitiArrList, WillUseHito1, EachHito2)) continue;
foreach (int EachHito3 in KouhoHitoArr.Where(X => X > EachHito2)) {
if (HasSameTrio(Popped.HitoHaitiArrList, WillUseHito1, EachHito3)) continue;
if (HasSameTrio(Popped.HitoHaitiArrList, EachHito2, EachHito3)) continue;
PushSyori(WillUseHito1, EachHito2, EachHito3);
}
}
}
}
//過去に同じトリオであったかを返す
static bool HasSameTrio(List<int[]> pHitoHaitiArrList, int pHito1, int pHito2)
{
foreach (int[] AnyHitoHaitiArr in pHitoHaitiArrList) {
int Ind = Array.IndexOf<int>(AnyHitoHaitiArr, pHito1);
Ind -= Ind % 3;
for (int I = Ind; I <= Ind + 2; I++) {
if (AnyHitoHaitiArr[I] == pHito2) return true;
}
}
return false;
}
//解を出力
static void PrintAnswer(List<int[]> pHitoHaitiArrList)
{
int DayCnt = 0;
foreach (int[] AnyHitoHaitiArr in pHitoHaitiArrList) {
Console.Write("Day{0} : ", ++DayCnt);
for (int I = 0; I <= UB; I++) {
if (I % 3 == 0) Console.Write('[');
Console.Write("{0,2}", AnyHitoHaitiArr[I]);
if (I % 3 == 2) Console.Write(']');
else Console.Write(',');
}
Console.WriteLine();
}
}
}
実行結果
1回目のPop処理を実行中。経過時間=00:00:00.0076543
100001回目のPop処理を実行中。経過時間=00:00:02.0417907
200001回目のPop処理を実行中。経過時間=00:00:04.0013146
300001回目のPop処理を実行中。経過時間=00:00:05.9640605
解を発見。経過時間=00:00:07.7342137
Day1 : [ 1, 2, 3][ 4, 5, 6][ 7, 8, 9][10,11,12][13,14,15]
Day2 : [ 1, 4,15][ 2,12,14][ 3, 9,13][ 5, 8,11][ 6, 7,10]
Day3 : [ 1, 5,14][ 2, 9,11][ 3, 6,12][ 4, 7,13][ 8,10,15]
Day4 : [ 1, 6, 9][ 2, 8,13][ 3, 5,10][ 4,11,14][ 7,12,15]
Day5 : [ 1,10,13][ 2, 5, 7][ 3,11,15][ 4, 9,12][ 6, 8,14]
Day6 : [ 1, 8,12][ 2, 4,10][ 3, 7,14][ 5, 9,15][ 6,11,13]
Day7 : [ 1, 7,11][ 2, 6,15][ 3, 4, 8][ 5,12,13][ 9,10,14]
解説
組み合わせを、1通り求めるだけの問題なので、
固定可能な条件を列挙して計算量を減らしてます。
1日目は、どう組み合わせても同じことなので1,2,3,4,5,6,7,8,9,10,11,12,13,14,15で固定とし、
各日の最初の人はどれでも同じなので1で固定としてます。
また、4,5,6は
それぞれ1と2日目、3日目、4日目に同じトリオになることで固定してます。
ちなみに本問は、カークマンの女生徒の問題と呼ばれてます。