C #, M = 2535
Questo implementa * il sistema che ho descritto matematicamente sul thread che ha provocato questo contest. Richiedo il bonus di 300 rep. Il programma esegue l'autotest se lo si esegue senza argomenti della riga di comando o con --test
come argomento della riga di comando; per spia 1, corri con --spy1
, e per spia 2 con --spy2
. In ogni caso, prende il numero che dovrei comunicare da stdin, quindi esegue i lanci tramite stdin e stdout.
* In realtà, ho trovato un'ottimizzazione che fa una differenza enorme (da diversi minuti per generare l'albero decisionale, a meno di un secondo); l'albero che genera è fondamentalmente lo stesso, ma sto ancora lavorando su una prova di ciò. Se si desidera un'implementazione diretta del sistema che ho descritto altrove, vedere la revisione 2 , anche se si potrebbe desiderare di eseguire il backport della registrazione extra da Main
e le migliori comunicazioni tra thread TestSpyIO
.
Se si desidera un caso di test che si completa in meno di un minuto, passare N
a 16
e M
a 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;
}
}
}
Istruzioni per utenti Linux
Dovrai mono-csc
compilare (sui sistemi basati su Debian, è nel mono-devel
pacchetto) ed mono
eseguire ( mono-runtime
pacchetto). Quindi sono gli incantesimi
mono-csc -out:codegolf31673.exe codegolf31673.cs
mono codegolf31673.exe --test
eccetera.