C#、M = 2535
これは、このコンテストを引き起こしたスレッドで数学的に説明したシステムを実装します。300 repボーナスを請求します。プログラム--test
は、コマンドライン引数なしで、またはコマンドライン引数として実行すると、自己テストします。スパイ1の場合はで実行し--spy1
、スパイ2の場合はで実行し--spy2
ます。いずれの場合も、stdinから通信する必要がある番号を取得し、stdinとstdoutを介してスローを実行します。
*実際、私は、大きな違いを生む最適化を見つけました(数分から決定木を生成するまで、1秒未満まで)。それが生成するツリーは基本的に同じですが、私はまだその証拠に取り組んでいます。他の場所で説明したシステムの直接実装が必要な場合は、リビジョン2を使用すると、より余分のロギングバックポートしたいかもしれませんが、Main
そしてより良いスレッド間の途切れをTestSpyIO
。
あなたが分未満で完了したテストケースをしたい場合は、変更N
する16
とM
します87
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace CodeGolf
{
internal class Puzzle625
{
public static void Main(string[] args)
{
const int N = 26;
const int M = 2535;
var root = BuildDecisionTree(N);
if (args.Length == 0 || args[0] == "--test")
{
DateTime startUtc = DateTime.UtcNow;
Console.WriteLine("Built decision tree in {0}", DateTime.UtcNow - startUtc);
startUtc = DateTime.UtcNow;
int ok = 0;
int fail = 0;
for (int i = 1; i <= M; i++)
{
for (int j = 1; j <= M; j++)
{
if (Test(i, j, root)) ok++;
else fail++;
}
double projectedTimeMillis = (DateTime.UtcNow - startUtc).TotalMilliseconds * M / i;
Console.WriteLine("Interim result: ok = {0}, fail = {1}, projected test time {2}", ok, fail, TimeSpan.FromMilliseconds(projectedTimeMillis));
}
Console.WriteLine("All tested: ok = {0}, fail = {1}, in {2}", ok, fail, DateTime.UtcNow - startUtc);
Console.ReadKey();
}
else if (args[0] == "--spy1")
{
new Spy(new ConsoleIO(), root, true).Run();
}
else if (args[0] == "--spy2")
{
new Spy(new ConsoleIO(), root, false).Run();
}
else
{
Console.WriteLine("Usage: Puzzle625.exe [--test|--spy1|--spy2]");
}
}
private static bool Test(int i, int j, Node root)
{
TestSpyIO io1 = new TestSpyIO("Spy 1");
TestSpyIO io2 = new TestSpyIO("Spy 2");
io1.Partner = io2;
io2.Partner = io1;
// HACK! Prime the input
io2.Output(i);
io1.Output(j);
Spy spy1 = new Spy(io1, root, true);
Spy spy2 = new Spy(io2, root, false);
Thread th1 = new Thread(spy1.Run);
Thread th2 = new Thread(spy2.Run);
th1.Start();
th2.Start();
th1.Join();
th2.Join();
// Check buffer contents. Spy 2 should output spy 1's value, so it's lurking in io1, and vice versa.
return io1.Input() == i && io2.Input() == j;
}
private static Node BuildDecisionTree(int numStones)
{
NodeValue[] trees = new NodeValue[] { NodeValue.Trivial };
for (int k = 2; k <= numStones; k++)
{
int[] prev = trees.Select(nv => nv.Y).ToArray();
List<int> row = new List<int>(prev);
int cap = prev.Length;
for (int i = 1; i <= prev[0]; i++)
{
while (prev[cap - 1] < i) cap--;
row.Add(cap);
}
int[] next = row.OrderByDescending(x => x).ToArray();
NodeValue[] nextTrees = new NodeValue[next.Length];
nextTrees[0] = trees.Last().Reverse();
for (int i = 1; i < next.Length; i++)
{
int cp = next[i] - 1;
nextTrees[i] = trees[cp].Combine(trees[i - prev[cp]]);
}
trees = nextTrees;
}
NodeValue best = trees.MaxElement(v => Math.Min(v.X, v.Y));
return BuildDecisionTree(numStones, best, new Dictionary<Pair<int, NodeValue>, Node>());
}
private static Node BuildDecisionTree(int numStones, NodeValue val, IDictionary<Pair<int, NodeValue>, Node> cache)
{
// Base cases
// NB We might get passed val null with 0 stones, so we hack around that
if (numStones == 0) return new Node(NodeValue.Trivial, new Node[0]);
// Cache
Pair<int, NodeValue> key = new Pair<int, NodeValue>(numStones, val);
Node node;
if (cache.TryGetValue(key, out node)) return node;
// The pair-of-nodes construction is based on a bijection between
// $\prod_{i<k} T_i \cup \{(\infty, 0)\}$
// and
// $(T_{k-1} \cup \{(\infty, 0)\}) \times \prod_{i<k-1} T_i \cup \{(\infty, 0)\}$
// val.Left represents the element of $T_{k-1} \cup \{(\infty, 0)\}$ (using null for the $(\infty, 0)$)
// and val.Right represents $\prod_{i<k-1} T_i \cup \{(\infty, 0)\}$ by bijection with $T_{k-1} \cup \{(\infty, 0)\}$.
// so val.Right.Left represents the element of $T_{k-2}$ and so on.
// The element of $T_{k-i}$ corresponds to throwing $i$ stones.
Node[] children = new Node[numStones];
NodeValue current = val;
for (int i = 0; i < numStones && current != null; i++)
{
children[i] = BuildDecisionTree(numStones - (i + 1), current.Left, cache);
current = current.Right;
}
node = new Node(val, children);
// Cache
cache[key] = node;
return node;
}
class Pair<TFirst, TSecond>
{
public readonly TFirst X;
public readonly TSecond Y;
public Pair(TFirst x, TSecond y)
{
this.X = x;
this.Y = y;
}
public override string ToString()
{
return string.Format("({0}, {1})", X, Y);
}
public override bool Equals(object obj)
{
Pair<TFirst, TSecond> other = obj as Pair<TFirst, TSecond>;
return other != null && object.Equals(other.X, this.X) && object.Equals(other.Y, this.Y);
}
public override int GetHashCode()
{
return X.GetHashCode() + 37 * Y.GetHashCode();
}
}
class NodeValue : Pair<int, int>
{
public readonly NodeValue Left;
public readonly NodeValue Right;
public static NodeValue Trivial = new NodeValue(1, 1, null, null);
private NodeValue(int x, int y, NodeValue left, NodeValue right) : base(x, y)
{
this.Left = left;
this.Right = right;
}
public NodeValue Reverse()
{
return new NodeValue(Y, X, this, null);
}
public NodeValue Combine(NodeValue other)
{
return new NodeValue(other.X + Y, Math.Min(other.Y, X), this, other);
}
}
class Node
{
public readonly NodeValue Value;
private readonly Node[] _Children;
public Node this[int n]
{
get { return _Children[n]; }
}
public int RemainingStones
{
get { return _Children.Length; }
}
public Node(NodeValue value, IEnumerable<Node> children)
{
this.Value = value;
this._Children = children.ToArray();
}
}
interface SpyIO
{
int Input();
void Output(int i);
}
// TODO The inter-thread communication here can almost certainly be much better
class TestSpyIO : SpyIO
{
private object _Lock = new object();
private int? _Buffer;
public TestSpyIO Partner;
public readonly string Name;
internal TestSpyIO(string name)
{
this.Name = name;
}
public int Input()
{
lock (_Lock)
{
while (!_Buffer.HasValue) Monitor.Wait(_Lock);
int rv = _Buffer.Value;
_Buffer = null;
Monitor.PulseAll(_Lock);
return rv;
}
}
public void Output(int i)
{
lock (Partner._Lock)
{
while (Partner._Buffer.HasValue) Monitor.Wait(Partner._Lock);
Partner._Buffer = i;
Monitor.PulseAll(Partner._Lock);
}
}
}
class ConsoleIO : SpyIO
{
public int Input()
{
return Convert.ToInt32(Console.ReadLine());
}
public void Output(int i)
{
Console.WriteLine("{0}", i);
}
}
class Spy
{
private readonly SpyIO _IO;
private Node _Node;
private bool _MyTurn;
internal Spy(SpyIO io, Node root, bool isSpy1)
{
this._IO = io;
this._Node = root;
this._MyTurn = isSpy1;
}
internal void Run()
{
int myValue = _IO.Input() - 1;
int hisValue = 1;
bool myTurn = _MyTurn;
Node n = _Node;
while (n.RemainingStones > 0)
{
if (myTurn)
{
if (myValue >= n.Value.X) throw new Exception("Internal error");
for (int i = 0; i < n.RemainingStones; i++)
{
// n[i] allows me to represent n[i].Y values: 0 to n[i].Y - 1
if (myValue < n[i].Value.Y)
{
_IO.Output(i + 1);
n = n[i];
break;
}
else myValue -= n[i].Value.Y;
}
}
else
{
int thrown = _IO.Input();
for (int i = 0; i < thrown - 1; i++)
{
hisValue += n[i].Value.Y;
}
n = n[thrown - 1];
}
myTurn = !myTurn;
}
_IO.Output(hisValue);
}
}
}
static class LinqExt
{
// I'm not sure why this isn't built into Linq.
public static TElement MaxElement<TElement>(this IEnumerable<TElement> e, Func<TElement, int> f)
{
int bestValue = int.MinValue;
TElement best = default(TElement);
foreach (var elt in e)
{
int value = f(elt);
if (value > bestValue)
{
bestValue = value;
best = elt;
}
}
return best;
}
}
}
Linuxユーザー向けの手順
あなたは必要がありますmono-csc
(それは中ですが、Debianベースのシステム上でコンパイルするmono-devel
パッケージ)とmono
(実行するためにmono-runtime
パッケージを)。それから呪文は
mono-csc -out:codegolf31673.exe codegolf31673.cs
mono codegolf31673.exe --test
等