AOJ本の読書メモ
AOJ
次のAOJの問題へ
前のAOJの問題へ
AOJ 0563 歩くサンタクロース
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("5 4");
WillReturn.Add("3");
WillReturn.Add("1 1");
WillReturn.Add("3 4");
WillReturn.Add("5 3");
//10
//3 3
}
else if (InputPattern == "Input2") {
WillReturn.Add("4 6");
WillReturn.Add("8");
WillReturn.Add("1 3");
WillReturn.Add("3 2");
WillReturn.Add("4 4");
WillReturn.Add("2 5");
WillReturn.Add("2 3");
WillReturn.Add("3 3");
WillReturn.Add("3 4");
WillReturn.Add("2 4");
//21
//2 3
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
struct PointDef
{
internal long X;
internal long Y;
}
static List<PointDef> mPointList = new List<PointDef>();
static long UB;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);
foreach (string EachStr in InputList.Skip(2)) {
SplitAct(EachStr);
PointDef WillAdd;
WillAdd.X = wkArr[0];
WillAdd.Y = wkArr[1];
mPointList.Add(WillAdd);
}
UB = mPointList.Count - 1;
// X座標の中央値付近の値Listを求める
var XArr = mPointList.Select(pX => pX.X).ToArray();
Array.Sort(XArr);
var NearMedianXList = DeriveNearMedianList(XArr);
// Y座標の中央値を求める
var YArr = mPointList.Select(pX => pX.Y).ToArray();
Array.Sort(YArr);
var NearMedianYList = DeriveNearMedianList(YArr);
long AnswerVal = long.MaxValue;
long AnswerX = -1;
long AnswerY = -1;
foreach (long EachX in NearMedianXList) {
foreach (long EachY in NearMedianYList) {
System.GC.Collect();
long AnswerKouho = DeriveAnswerKouho(EachX, EachY);
if (AnswerKouho < AnswerVal) {
AnswerVal = AnswerKouho;
AnswerX = EachX;
AnswerY = EachY;
}
}
}
Console.WriteLine(AnswerVal);
Console.WriteLine("{0} {1}", AnswerX, AnswerY);
}
// 基点とする座標を引数として解候補を返す
static long DeriveAnswerKouho(long pBaseX, long pBaseY)
{
PointDef[] SortedArr =
mPointList.OrderBy(pX => Math.Abs(pBaseX - pX.X) + Math.Abs(pBaseY - pX.Y)).ToArray();
long Answer = 0;
for (long I = 0; I <= UB; I++) {
long Diff = Math.Abs(pBaseX - SortedArr[I].X) + Math.Abs(pBaseY - SortedArr[I].Y);
if (I < UB) {
Answer += Diff * 2;
}
else {
Answer += Diff;
}
}
return Answer;
}
// 配列を引数として、中央値付近の値Listを返す
static List<long> DeriveNearMedianList(long[] pArr)
{
var NearMedianList = new List<long>();
Predicate<long> IsValidInd = (pInd) =>
{
return 0 <= pInd && pInd <= UB;
};
long MidInd = UB / 2;
if (IsValidInd(MidInd - 1)) NearMedianList.Add(pArr[MidInd - 1]);
if (IsValidInd(MidInd)) NearMedianList.Add(pArr[MidInd]);
if (IsValidInd(MidInd + 1)) NearMedianList.Add(pArr[MidInd + 1]);
return NearMedianList;
}
}
解説
まずは、縦と横を独立に考え、横だけで考えます。
A B C D
基準点は、BからCの間になります。
Bより左ならば、右に基準点を動かすほど得であり、
Cより右ならば、左に基準点を動かすほど得だからです。
後は、片道で済む移動を考えて、
BからCの間のどこを基準点にするのが最適かを考える必要がありますが
これは、結局は、最も遠い点までの距離を最小化できれば最適なので、
BとCの両方を候補にし、縦方向も同様とし、
全組み合わせを最もコストの安い基準点を採用すれば良いです。