using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
const int TeiwaKagen = 20;
const int TeiwaJyougen = 60;
static System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
static void Main()
{
//定和でループ
for (int Teiwa = TeiwaKagen; Teiwa <= TeiwaJyougen; Teiwa++) {
//定和を引数として、20個の数の候補を列挙
List<int[]> KouhoArrList = DeriveKouhoArrList(Teiwa);
Console.WriteLine("定和={0}での20個の数の候補は、{1}通り", Teiwa, KouhoArrList.Count);
bool FoundAnswer = false;
foreach (int[] EachIntArr in KouhoArrList) {
FoundAnswer = FindAnswer(Teiwa, EachIntArr);
if (FoundAnswer) break;
}
if (FoundAnswer) break;
}
}
struct JyoutaiDef1
{
internal int CurrNum;
internal int SumNum;
internal List<int> NumList;
}
//定和を引数として、20個の数の候補を列挙
static List<int[]> DeriveKouhoArrList(int pTeiwa)
{
var WillReturnList = new List<int[]>();
var stk = new Stack<JyoutaiDef1>();
JyoutaiDef1 WillPush;
for (int I = 1; I <= pTeiwa; I++) {
WillPush.CurrNum = WillPush.SumNum = I;
WillPush.NumList = new List<int>() { I };
stk.Push(WillPush);
}
while (stk.Count > 0) {
JyoutaiDef1 Popped = stk.Pop();
//クリア判定
if (Popped.NumList.Count == 20) {
//A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T != 定和の4倍でない場合
if (Popped.SumNum != pTeiwa * 4) continue;
WillReturnList.Add(Popped.NumList.ToArray());
continue;
}
for (int I = Popped.CurrNum + 1; I <= pTeiwa; I++) {
int RestCnt = 20 - Popped.NumList.Count;
//定和の4倍を超えるなら枝切り
if (Popped.SumNum + RestCnt * I > pTeiwa * 4) break;
WillPush.CurrNum = I;
WillPush.SumNum = Popped.SumNum + I;
WillPush.NumList = new List<int>(Popped.NumList) { I };
stk.Push(WillPush);
}
}
return WillReturnList;
}
struct JyoutaiDef2
{
internal Dictionary<char, int> NumDict;
}
//20個の数の候補を引数として、解が存在するかを返す
static bool FindAnswer(int pTeiwa, int[] pKouhoArr)
{
var stk = new Stack<JyoutaiDef2>();
JyoutaiDef2 WillPush;
WillPush.NumDict = new Dictionary<char, int>();
//対称解の除外で、Aは20個の数の中の最小値とする
WillPush.NumDict.Add('A', pKouhoArr.Min());
stk.Push(WillPush);
while (stk.Count > 0) {
JyoutaiDef2 Popped = stk.Pop();
//クリア判定
if (Popped.NumDict.Count == 20) {
Console.WriteLine("解を発見。経過時間={0}", sw.Elapsed);
foreach (var EachPair in Popped.NumDict.OrderBy(X => X.Key)) {
Console.WriteLine("{0}={1}", EachPair.Key, EachPair.Value);
}
return true;
}
foreach (int EachInt in pKouhoArr) {
//数値の再使用は不可
if (Popped.NumDict.ContainsValue(EachInt)) continue;
char CurrChar = (char)(Popped.NumDict.Keys.Max() + 1);
//対称解の除外で、B<Cとする
if (CurrChar == 'C' && Popped.NumDict['B'] > EachInt) continue;
WillPush.NumDict = new Dictionary<char, int>(Popped.NumDict);
WillPush.NumDict.Add(CurrChar, EachInt);
//枝切りの判定
if (WillEdakiri(pTeiwa, WillPush.NumDict) == false)
stk.Push(WillPush);
}
}
return false;
}
//枝切りの判定
static bool WillEdakiri(int pTeiwa, Dictionary<char, int> pNumDict)
{
if (WillEdakiriHelper(pTeiwa, pNumDict, 'A', 'B', 'C', 'E', 'F')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'E', 'F', 'K', 'Q', 'L')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'C', 'F', 'G', 'L', 'M')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'L', 'M', 'Q', 'T', 'R')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'A', 'C', 'D', 'G', 'H')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'G', 'H', 'M', 'R', 'N')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'H', 'D', 'I', 'N', 'O')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'N', 'O', 'R', 'T', 'S')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'A', 'D', 'B', 'I', 'J')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'I', 'J', 'O', 'S', 'P')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'B', 'J', 'E', 'P', 'K')) return true;
if (WillEdakiriHelper(pTeiwa, pNumDict, 'P', 'K', 'S', 'Q', 'T')) return true;
return false;
}
//枝切りの判定のヘルパメソッド
static bool WillEdakiriHelper(int pTeiwa, Dictionary<char, int> pNumDict,
char p1, char p2, char p3, char p4, char p5)
{
int Cnt = 0;
int SumVal = 0;
if (pNumDict.ContainsKey(p1)) { SumVal += pNumDict[p1]; Cnt++; }
if (pNumDict.ContainsKey(p2)) { SumVal += pNumDict[p2]; Cnt++; }
if (pNumDict.ContainsKey(p3)) { SumVal += pNumDict[p3]; Cnt++; }
if (pNumDict.ContainsKey(p4)) { SumVal += pNumDict[p4]; Cnt++; }
if (pNumDict.ContainsKey(p5)) { SumVal += pNumDict[p5]; Cnt++; }
if (Cnt == 5)
return SumVal != pTeiwa;
return SumVal >= pTeiwa;
}
}