using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
const int UB = 7 - 1;
const int LimitLevel = 7;
const int HalfTansakuMaxSumLength = 6 + 4 + 6 * 5;
const int AnswerLengthKagen = 58;
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
Console.WriteLine("正方向で半分全探索します。");
IEnumerable<JyoutaiDef> SeiArr = ExecDFS(true);
Console.WriteLine("メモリ使用量={0}Kバイト", GC.GetTotalMemory(false) / 1024);
Console.WriteLine("逆方向で半分全探索します。");
JyoutaiDef[] RevArr = ExecDFS(false).OrderByDescending(X => X.SumLength).ToArray();
Console.WriteLine("メモリ使用量={0}Kバイト", GC.GetTotalMemory(false) / 1024);
int AnswerLength = 0;
int AnswerPosCnt = 0;
foreach (JyoutaiDef EachSei in SeiArr) {
for (int I = 0; I <= RevArr.GetUpperBound(0); I++) {
if (EachSei.SumLength + RevArr[I].SumLength < AnswerLength) break;
if (EachSei.CurrX != RevArr[I].CurrX) continue;
if (EachSei.CurrY != RevArr[I].CurrY) continue;
if (EachSei.UsedRoadSet.Overlaps(RevArr[I].UsedRoadSet))
continue;
int wkLength = EachSei.SumLength + RevArr[I].SumLength;
if (wkLength < AnswerLength) continue;
else if (wkLength > AnswerLength) AnswerPosCnt = 0;
AnswerLength = wkLength;
var wkSet = new HashSet<int>(EachSei.PosSet);
wkSet.UnionWith(RevArr[I].PosSet);
if (wkSet.Count < AnswerPosCnt) continue;
AnswerPosCnt = wkSet.Count;
Console.WriteLine("解候補を発見。長さ{0}で点の数={1}", AnswerLength, AnswerPosCnt);
var AnswerList = new List<string>(EachSei.Log);
AnswerList.AddRange(RevArr[I].Log.AsEnumerable().Reverse().Skip(1));
for (int J = 0; J <= AnswerList.Count - 1; J++) {
Console.Write(AnswerList[J]);
if (J == 9 || J == AnswerList.Count - 1) Console.WriteLine();
else Console.Write(",");
}
}
}
Console.WriteLine("経過時間={0}", sw.Elapsed);
}
struct JyoutaiDef
{
internal int Muki; //0が上向き,1が左向き,2が下向き,3が右向き
internal int CurrX;
internal int CurrY;
internal int Level; //曲がった回数
internal HashSet<int> PosSet;
internal int SumLength;
internal HashSet<int> UsedRoadSet;
internal List<string> Log;
}
//双方向に半分全探索
static IEnumerable<JyoutaiDef> ExecDFS(bool pIsSei)
{
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
if (pIsSei) {
WillPush.Muki = 2;
WillPush.CurrX = 2;
WillPush.CurrY = 0;
WillPush.PosSet = new HashSet<int>();
WillPush.PosSet.Add(10 * WillPush.CurrX + WillPush.CurrY);
WillPush.Log = new List<string>();
WillPush.Log.Add(string.Format("({0},{1})", WillPush.CurrX, WillPush.CurrY));
}
else {
WillPush.Muki = 1;
WillPush.CurrX = 6;
WillPush.CurrY = 2;
WillPush.PosSet = new HashSet<int>();
WillPush.PosSet.Add(10 * WillPush.CurrX + WillPush.CurrY);
WillPush.Log = new List<string>();
WillPush.Log.Add(string.Format("({0},{1})", WillPush.CurrX, WillPush.CurrY));
}
WillPush.Level = 0;
WillPush.SumLength = 0;
WillPush.UsedRoadSet = new HashSet<int>();
Stk.Push(WillPush);
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
//クリア判定
if (Popped.Level == LimitLevel) {
//Mukiが違うだけの葉ノードを除外
if (Popped.Muki != 0 && Popped.Muki != 1) continue;
//長さ合計が、解の下限未満なら、枝切り
if (HalfTansakuMaxSumLength + Popped.SumLength < AnswerLengthKagen)
continue;
yield return Popped;
continue;
}
int HeniX = 0, HeniY = 0;
if (Popped.Muki == 0) HeniY = -1; //上向き
if (Popped.Muki == 1) HeniX = -1; //左向き
if (Popped.Muki == 2) HeniY = 1; //下向き
if (Popped.Muki == 3) HeniX = 1; //右向き
WillPush.CurrX = Popped.CurrX;
WillPush.CurrY = Popped.CurrY;
var wkPosSet = new HashSet<int>();
var wkUsedRoadSet = new HashSet<int>();
while (true) {
int PrevX = WillPush.CurrX;
int PrevY = WillPush.CurrY;
WillPush.CurrX += HeniX;
WillPush.CurrY += HeniY;
if (WillPush.CurrX < 0 || UB < WillPush.CurrX) break;
if (WillPush.CurrY < 0 || UB < WillPush.CurrY) break;
//使用済の道路は使用不可
int wkUsedRoadInt = UsedRoadToInt(PrevX, PrevY, WillPush.CurrX, WillPush.CurrY);
if (Popped.UsedRoadSet.Contains(wkUsedRoadInt)) break;
wkUsedRoadSet.Add(wkUsedRoadInt);
wkPosSet.Add((byte)(10 * WillPush.CurrX + WillPush.CurrY));
WillPush.Level = Popped.Level + 1;
WillPush.PosSet = new HashSet<int>(Popped.PosSet);
WillPush.PosSet.UnionWith(wkPosSet);
WillPush.SumLength = Popped.SumLength + wkPosSet.Count;
WillPush.UsedRoadSet = new HashSet<int>(Popped.UsedRoadSet);
WillPush.UsedRoadSet.UnionWith(wkUsedRoadSet);
WillPush.Log = new List<string>(Popped.Log);
WillPush.Log.Add(string.Format("({0},{1})", WillPush.CurrX, WillPush.CurrY));
//左に曲がる場合
WillPush.Muki = (Popped.Muki + 1) % 4;
Stk.Push(WillPush);
//右に曲がる場合
WillPush.Muki = (Popped.Muki - 1 + 4) % 4;
Stk.Push(WillPush);
}
}
}
//使用した道路をInt型で表現
static int UsedRoadToInt(int pFromX, int pFromY, int pToX, int pToY)
{
int MinX = Math.Min(pFromX, pToX);
int MaxX = Math.Max(pFromX, pToX);
int MinY = Math.Min(pFromY, pToY);
int MaxY = Math.Max(pFromY, pToY);
return 1000 * MinX + 100 * MinY + 10 * MaxX + MaxY;
}
}
省略
解候補を発見。長さ58で点の数=48
(2,0),(2,5),(5,5),(5,1),(1,1),(1,4),(6,4),(6,6),(0,6),(0,3)
(6,3),(6,0),(0,0),(0,2),(6,2)
解候補を発見。長さ58で点の数=48
(2,0),(2,4),(6,4),(6,6),(0,6),(0,3),(6,3),(6,0),(1,0),(1,5)
(5,5),(5,1),(0,1),(0,2),(6,2)
解候補を発見。長さ58で点の数=48
(2,0),(2,4),(6,4),(6,6),(0,6),(0,3),(6,3),(6,1),(1,1),(1,5)
(5,5),(5,0),(0,0),(0,2),(6,2)
解候補を発見。長さ58で点の数=48
(2,0),(2,4),(6,4),(6,6),(1,6),(1,1),(5,1),(5,5),(0,5),(0,3)
(6,3),(6,0),(0,0),(0,2),(6,2)
解候補を発見。長さ58で点の数=48
(2,0),(2,4),(6,4),(6,5),(1,5),(1,1),(5,1),(5,6),(0,6),(0,3)
(6,3),(6,0),(0,0),(0,2),(6,2)
経過時間=00:00:05.9866823