典型問題
次の典型問題へ
前の典型問題へ
典型問題(初級) C 巡回セールスマン問題
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("4");
WillReturn.Add("0 3 1 4");
WillReturn.Add("1 0 5 9");
WillReturn.Add("2 6 0 5");
WillReturn.Add("3 5 8 0");
//12
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int mN;
static int UB;
static int[,] mMatrixArr;
static void Main()
{
List<string> InputList = GetInputList();
mN = int.Parse(InputList[0]);
UB = mN - 1;
mMatrixArr = new int[UB + 1, UB + 1];
int[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(X => int.Parse(X)).ToArray();
for (int Y = 0; Y <= UB; Y++) {
SplitAct(InputList[Y + 1]);
for (int X = 0; X <= UB; X++) {
mMatrixArr[X, Y] = wkArr[X];
}
}
Solve();
}
struct JyoutaiDef
{
internal int CurrNode;
internal HashSet<int> VisitedSet;
internal long 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, long>();
long Answer = long.MaxValue;
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
// クリア判定
if (Popped.VisitedSet.Count == mN) {
if (Popped.CurrNode == 0) {
Answer = Math.Min(Answer, Popped.CostSum);
}
continue;
}
// 解でないのに、ノード0を訪問してたら枝切り
if (Popped.VisitedSet.Contains(0)) {
continue;
}
for (int X = 0; X <= UB; X++) {
//再訪問防止
if (Popped.VisitedSet.Contains(X)) {
continue;
}
WillPush.CurrNode = X;
WillPush.CostSum = Popped.CostSum + mMatrixArr[X, Popped.CurrNode];
// 枝切り
if (WillPush.CostSum >= Answer) {
continue;
}
WillPush.VisitedSet = new HashSet<int>(Popped.VisitedSet);
WillPush.VisitedSet.Add(X);
int Hash = GetHash(WillPush.VisitedSet, WillPush.CurrNode);
if (MinCostDict.ContainsKey(Hash)) {
if (MinCostDict[Hash] <= WillPush.CostSum) {
continue;
}
}
MinCostDict[Hash] = WillPush.CostSum;
Stk.Push(WillPush);
}
}
Console.WriteLine(Answer);
}
// 訪問済ノードと現在ノードから、ハッシュ値を求める
// 24ビット使用し、
// 先頭16ビットが各ノードの訪問有無
// 末尾8ビットがpCurrNode
static int GetHash(HashSet<int> pVisitedSet, int pCurrNode)
{
int WillReturn = 0;
int Base = 1;
for (int I = 0; I <= mN - 1; I++) {
if (pVisitedSet.Contains(I)) {
WillReturn += Base;
}
Base *= 2;
}
// 8ビット左シフト
WillReturn <<= 8;
WillReturn += pCurrNode;
return WillReturn;
}
}
解説
訪問済ノードと現在ノードから
ハッシュ値を求めて、その時点でのコストでの枝切りや
解候補のコストを超えてないかの枝切りを行ってます。
類題