AOJ本の読書メモ
AOJ
次のAOJの問題へ
前のAOJの問題へ
DPL_2_A: Traveling Salesman Problem
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
// Q080 TSP問題 https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_2_A&lang=jp
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("4 6");
WillReturn.Add("0 1 2");
WillReturn.Add("1 2 3");
WillReturn.Add("1 3 9");
WillReturn.Add("2 0 1");
WillReturn.Add("2 3 6");
WillReturn.Add("3 2 4");
//16
}
else if (InputPattern == "Input2") {
WillReturn.Add("3 3");
WillReturn.Add("0 1 1");
WillReturn.Add("1 2 1");
WillReturn.Add("0 2 1");
//-1
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
struct EdgeInfoDef
{
internal int ToNode;
internal int Cost;
}
// 隣接リスト
static Dictionary<int, List<EdgeInfoDef>> mEdgeInfoListDict = new Dictionary<int, List<EdgeInfoDef>>();
static int mV;
static void Main()
{
List<string> InputList = GetInputList();
int[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();
SplitAct(InputList[0]);
mV = wkArr[0];
foreach (string EachStr in InputList.Skip(1)) {
SplitAct(EachStr);
int S = wkArr[0];
int T = wkArr[1];
int D = wkArr[2];
if (mEdgeInfoListDict.ContainsKey(S) == false) {
mEdgeInfoListDict[S] = new List<EdgeInfoDef>();
}
mEdgeInfoListDict[S].Add(new EdgeInfoDef { ToNode = T, Cost = D });
}
Solve();
}
struct JyoutaiDef
{
internal int CurrNode;
internal HashSet<int> VisitedSet;
internal int CostSum;
}
static void Solve()
{
// 開始ノードは0とする
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.CurrNode = 0;
WillPush.VisitedSet = new HashSet<int>();
WillPush.CostSum = 0;
Stk.Push(WillPush);
// 最小コスト[訪問済ノードと現在ノードのハッシュ値]
var MinCostDict = new Dictionary<int, int>();
bool FoundAnswer = false;
int Answer = int.MaxValue;
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
// クリア判定
if (Popped.VisitedSet.Count == mV) {
if (Popped.CurrNode == 0) {
FoundAnswer = true;
Answer = Math.Min(Answer, Popped.CostSum);
}
continue;
}
// 解でないのに、ノード0を訪問してたら枝切り
if (Popped.VisitedSet.Contains(0)) {
continue;
}
if (mEdgeInfoListDict.ContainsKey(Popped.CurrNode)) {
foreach (EdgeInfoDef EachEdgeInfo in mEdgeInfoListDict[Popped.CurrNode]) {
//再訪問防止
if (Popped.VisitedSet.Contains(EachEdgeInfo.ToNode)) {
continue;
}
WillPush.CurrNode = EachEdgeInfo.ToNode;
WillPush.CostSum = Popped.CostSum + EachEdgeInfo.Cost;
// 枝切り
if (FoundAnswer && WillPush.CostSum >= Answer) {
continue;
}
WillPush.VisitedSet = new HashSet<int>(Popped.VisitedSet);
WillPush.VisitedSet.Add(EachEdgeInfo.ToNode);
int Hash = GetHash(WillPush.VisitedSet, WillPush.CurrNode);
if (MinCostDict.ContainsKey(Hash)) {
if (MinCostDict[Hash] <= WillPush.CostSum) {
continue;
}
}
MinCostDict[Hash] = WillPush.CostSum;
Stk.Push(WillPush);
}
}
}
if (FoundAnswer) {
Console.WriteLine(Answer);
}
else {
Console.WriteLine(-1);
}
}
// 訪問済ノードと現在ノードから、ハッシュ値を求める
// 24ビット使用し、
// 先頭16ビットが各ノードの訪問有無
// 末尾8ビットがpCurrNode
static int GetHash(HashSet<int> pVisitedSet, int pCurrNode)
{
int WillReturn = 0;
int Base = 1;
for (int I = 0; I <= mV; I++) {
if (pVisitedSet.Contains(I)) {
WillReturn += Base;
}
Base *= 2;
}
// 8ビット左シフト
WillReturn <<= 8;
WillReturn += pCurrNode;
return WillReturn;
}
}
解説
訪問済ノードと現在ノードから
ハッシュ値を求めて、その時点でのコストでの枝切りや
解候補のコストを超えてないかの枝切りを行ってます。