Vereinfachtes Zugset

27

Es gibt viele verschiedene Arten von Zugsets, angefangen von Holzschienen wie Brio bis hin zur volldigitalen Steuerung perfekter, winziger Metallnachbildungen echter Züge. Für alle muss jedoch ein Gleis entworfen werden, in dem möglichst viele Ihrer Teile idealerweise verwendet werden.

Ihre Aufgabe ist es also, zu bestimmen, ob es bei gegebener Eingabe der verfügbaren Teile möglich ist, einen vollständigen geschlossenen Kreislauf unter Verwendung aller Elemente zu bilden, und wenn nicht, wie viele Teile vom maximal möglichen Kreislauf übrig bleiben.

Da dies ein vereinfachter Zugsatz ist, gibt es nur drei Elemente: große Kurve, kleine Kurve und gerade. Diese basieren alle auf einem quadratischen Raster:

Quadratisches Gitter mit großer und kleiner Kurve

  • "Big Curve" ist eine 90-Grad-Ecke, die 2 Einheiten in jeder Dimension abdeckt
  • "Little Curve" ist eine 90-Grad-Ecke, die eine Einheit in jede Richtung abdeckt
  • "Straight" ist ein gerades Element mit einer Länge von 1 Einheit

Dies bedeutet, dass der minimal mögliche Kreis aus 4 kleinen Kurven besteht - es ist ein Kreis mit einem Radius von 1 Einheit. Dies kann durch Hinzufügen von Paaren von geraden Elementen erweitert werden, um verschiedene Ovale zu bilden. Es sind auch andere Schaltungen möglich, indem Sie mehr Kurven hinzufügen oder die Kurventypen mischen.

Dieses Zugset enthält keine Kreuzungen oder Methoden zum Überqueren von Gleisen. Daher ist es nicht zulässig, dass zwei Elemente an dasselbe Ende eines anderen Elements angeschlossen werden (keine Y-Formationen) oder sich überkreuzen (keine X-Formationen). . Außerdem handelt es sich um einen Zugsatz, sodass jede Formation, die das Überholen eines Zugs nicht zulässt, ungültig ist. Beispiele hierfür sind Geraden, die sich in einem Winkel von 90 Grad treffen (es muss immer eine Kurve zwischen senkrechten Geraden geben) und Kurven, die sich in einem Winkel von 90 Grad treffen (Kurven müssen fließen).

Sie möchten auch so viele Teile wie möglich verwenden und dabei die Art der Teile ignorieren. Daher entscheiden Sie sich immer für eine Schaltung mit mehr Bits. Schließlich haben Sie nur einen Zug. Daher ist jede Lösung, die zu mehreren Schaltungen führt, inakzeptabel .

Eingang

Entweder ein Array mit drei Ganzzahlen, die alle größer oder gleich 0 sind und der Anzahl der verfügbaren großen Kurven, kleinen Kurven und Geraden entsprechen, oder Parameter, die in derselben Reihenfolge an Ihr Programm übergeben werden.

Ausgabe

Eine Zahl, die der Anzahl der verbleibenden Teile entspricht, wenn die maximal mögliche Schaltung für die bereitgestellten Elemente konstruiert wird.

Testdaten

Minimal circuit using big curves
Input: [4,0,0]
Output: 0

Slightly more complicated circuit
Input: [3,1,2]
Output: 0

Incomplete circuit - can't join
Input: [3,0,0]
Output: 3

Incomplete circuit - can't join
Input: [3,1,1]
Output: 5

Circuit where big curves share a centre
Input: [2,2,0]
Output: 0

Bigger circuit
Input: [2,6,4]
Output: 0

Circuit where both concave and convex curves required
Input: [8,0,0] or [0,8,0]
Output: 0

Circuit with left over bit
Input: [5,0,0] or [0,5,0]
Output: 1

Anmerkungen

  • 2 Geraden und eine kleine Kurve sind gleichbedeutend mit einer großen Kurve, verwenden Sie jedoch mehr Teile. Daher sollten Sie diese Kombination auf keinen Fall verwenden, wenn die Rennstrecke große Kurven enthält
  • 4 kleine Kurven können normalerweise gegen 4 Geraden getauscht werden, aber nicht, wenn dies dazu führen würde, dass sich die Rennstrecke selbst schneidet
  • Das Zugset ist ebenfalls idealisiert: Die Gleiselemente nehmen die gezeigten Breiten ein, sodass Kurven in einigen Fällen durch ein einzelnes Gitternetzquadrat verlaufen können, ohne sich zu schneiden. Das Raster definiert nur die Elementabmessungen. Insbesondere können zwei große Kurven so platziert werden, dass das Gitterquadrat oben links im Beispieldiagramm auch das untere rechte Quadrat einer anderen großen Kurve ist, die von links nach oben verläuft (wobei das Diagramm eine Kurve von rechts nach unten zeigt).
  • Eine kleine Kurve kann in den leeren Raum unter einer großen Kurve passen (rechtes unteres Gitterquadrat oben). Eine zweite große Kurve könnte auch diesen Raum nutzen, der gegenüber der ersten um eins verschoben ist
  • Eine kleine Kurve kann nicht auf denselben Rasterplatz passen wie die Außenseite einer großen Kurve - hauptsächlich, weil es keine Möglichkeit gibt, eine Verbindung herzustellen, die sich nicht illegal schneidet
Matthew
quelle
Also die Ausgabe für [5,0,0]oder [0,5,0]wäre 1. Ist das korrekt? Könnten Sie einen solchen Testfall hinzufügen?
Arnauld
@arnauld Ja, das stimmt. Sollte immer die verbleibende Anzahl von Elementen nach dem Aufbau der längsten möglichen Schaltung sein.
Matthew
Könnten Sie bitte bestätigen, dass dies eine Lösung für [8,0,0]zwei 2x2-Elemente ist, die sich in der Mitte des Gitters überlappen?
Arnauld
Ja, das ist die erwartete Lösung für diesen Testfall.
Matthew
Mir ist nicht klar, wie die Selbstkreuzung funktioniert. Könnten Sie präziser definieren, was erlaubt und was verboten ist?
Weizen-Assistent

Antworten:

9

[JavaScript (Node.js)], 1220 Byte

f=r=>{var a=[{n:0,d:[[0,-1,"0000000101011"],[1,-1,"0011111111111"],[0,0,"0111101111111"],[1,0,"1100010000000"]],e:[2,-1,1]},{n:0,d:[[-1,-1,"1001111111111"],[0,-1,"0000010010110"],[-1,0,"0110000100000"],[0,0,"1101111011111"]],e:[-2,-1,3]},{n:1,d:[[0,0,"0011101111111"]],e:[1,0,1]},{n:1,d:[[0,0,"1001111011111"]],e:[-1,0,3]},{n:2,d:[[0,0,"1111101011111"]],e:[0,-1,0]}],e=r=>{var a=r.d,e=r.e,n=[];return a.forEach(r=>{var a=r[2];n.push([-r[1],r[0],""+a[10]+a[5]+a[0]+a[8]+a[3]+a[11]+a[6]+a[1]+a[9]+a[4]+a[12]+a[7]+a[2]])}),{d:n,e:[-e[1],e[0],e[2]]}};i=((r,a)=>{for(var n=0;n<r.d;n++,a=e(a));var p=!1;return a.d.forEach(a=>{var e=r[`${r.p.x+a[0]},${r.p.y+a[1]}`];void 0===e&&(e="00000000000000");for(var n="",d=0;d<13;d++)"1"===e[d]&&"1"===a[2][d]&&(p=!0),n+=e[d]===a[2][d]?e[d]:"1";r[`${r.p.x+a[0]},${r.p.y+a[1]}`]=n}),r.p.x+=a.e[0],r.p.y+=a.e[1],r.d=(r.d+a.e[2])%4,!p});var n=[],p=(r,e)=>{a.forEach(a=>{var d=Object.assign({},r);if(d.p=Object.assign({},r.p),!(e[a.n]<=0)&&i(d,a)){if(d.ps+=a.n,0==d.p.x&&0==d.p.y&&0==d.d)return void n.push(d);var s=Object.assign([],e);s[a.n]-=1,p(d,s)}})};p({p:{x:0,y:0},d:0,ps:""},Object.assign([],r));var d=0;n.forEach(r=>{r.ps.length>d&&(d=r.ps.length)}),console.log(r[0]+r[1]+r[2]-d)};

Probieren Sie es online!

Hinweis: Der Eingang ist eigentlich die Variable q am Anfang. [2,6,4] wird auch einige Zeit in Anspruch nehmen, da dies eine Brute-Force-Lösung ohne Optimierungen ist.

Ich habe das tatsächlich getan, weil es seit über einem Jahr nicht mehr beantwortet wurde und ich war nur ein bisschen neugierig, ob es möglich war.


Ursprünglicher Code:

var q = [4, 2, 4];
var t = [
    {
        n: 0,
        d: [
            [0, -1, "0000000101011"],
            [1, -1, "0011111111111"],
            [0, 0, "0111101111111"],
            [1, 0, "1100010000000"]
        ],
        e: [2, -1, 1],

    },
    {
        n: 0,
        d: [
            [-1, -1, "1001111111111"],
            [0, -1, "0000010010110"],
            [-1, 0, "0110000100000"],
            [0, 0, "1101111011111"]
        ],
        e: [-2, -1, 3]
    },
    {
        n: 1,
        d: [
            [0, 0, "0011101111111"]
        ],
        e: [1, 0, 1]
    },
    {
        n: 1,
        d: [
            [0, 0, "1001111011111"]
        ],
        e: [-1, 0, 3]
    },
    {
        n: 2,
        d: [
            [0, 0, "1111101011111"]
        ],
        e: [0, -1, 0]
    },
];

r = (p) => {
    var d = p.d; var e = p.e; var o = [];
    d.forEach(i => {
        var d = i[2];
        o.push([-i[1], i[0], "" + d[10] + d[5] + d[0] + d[8] + d[3] + d[11] + d[6] + d[1] + d[9] + d[4] + d[12] + d[7] + d[2]])
    });
    return { d: o, e: [-e[1], e[0], e[2]] };
};

i = (g, p) => {
    //console.log(g.p, g.d);
    for (var i = 0; i < g.d; i++ , p = r(p));
    var c = false;
    p.d.forEach(d => {
        var v = g[`${g.p.x + d[0]},${g.p.y + d[1]}`];
        if (v === undefined) v = "00000000000000";
        var o = "";
        for (var i = 0; i < 13; i++) {
            if (v[i] === '1' && d[2][i] === '1')
                c = true;
            o += (v[i] === d[2][i]) ? v[i] : '1';
        }
        //console.log(o);
        g[`${g.p.x + d[0]},${g.p.y + d[1]}`] = o;
    });
    g.p.x += p.e[0];
    g.p.y += p.e[1];
    g.d = (g.d + p.e[2]) % 4;
    return !c;
};

var l = [];
var re = (g, p) => {
    t.forEach(piece => {
        var gr = Object.assign({}, g);
        gr.p = Object.assign({}, g.p);
        if (p[piece.n] <= 0)
            return;
        if (i(gr, piece)) {
            gr.ps += piece.n;
            if (gr.p.x == 0 && gr.p.y == 0 && gr.d == 0) {
                l.push(gr);
                return;
            }
            var ti = Object.assign([], p);
            ti[piece.n] -= 1;
            re(gr, ti);
        }
    });
};
var gr = { p: { x: 0, y: 0 }, d: 0, ps: "" };
re(gr, Object.assign([], q));

var c = 0;
var lo = 0;
l.forEach(g => {
    if (g.ps.length > lo) {
        require("./draw.js")(g, `outs/out${c++}.png`)
        lo = g.ps.length;
    }
});

console.log(q[0] + q[1] + q[2] - lo);

Zuerst sollte ich eine Grafik der verwendeten Kacheln beifügen.

Fliesen verwendet

The sections of this tile were given a number and
used for comparison and overlap handling later.

So there first thing is the array t at the start. 
This is a collection of track pieces that contain
    n[ame]: the index of the input array.
    d[ata]: the offset from the current tile and the Tile bit values.
    e[nd]: the relative offset and rotation that the piece provides.

function r[otate] ( p[iece] )
    this outputs a piece that is rotated by 90 degrees
    by rearranging the tile bits and the end offset

function i[nsert] ( g[rid], p[iece] )
    this modifies the passed in grid trying to place down each tile of the piece.
    if it hits a point where 2 tiles intersect it sets a flag c[ollision]
    it then adjusts the current p[osition] and and d[irection] stored in the grid.
    then it returns !c[ollision]

function re[peat] ( g[rid], p[eices] )
    this iterates across all nodes which
        creates a copy of the g[rid] as gr[id].
        checks if the piece is available if not continue
        if the peice is added without a collision
            add piece name to gr[id].ps[piece string];
            it checks if its back at the start
                add gr[id] to l[ist]
                return as no more pieces can be added without a collision.
            clone peices remove the used peice ti[nput]
            call re[peate] (gr[id], ti[nput])

call re[peate] with empty grid

search l[ist] for longest piece string
and output input added together minus the length of the longest string.

Tut mir leid, wenn der Artikel schwer zu lesen ist. Ich bin es nicht gewohnt zu erklären, wie mein Code funktioniert.

PS Ich habe auch ein paar Funktionen zum Zeichnen der Karten zu einem PNG gemacht, aber diese wurden natürlich entfernt, um zumindest etwas Platz zu sparen.

Cieric
quelle
Ich bin beeindruckt - ich habe die Hoffnung aufgegeben! Wäre interessiert an einer Zuschreibung
Matthew
@Matthew Ich werde sehen, wann ich Zeit habe, eine zu schreiben. Es könnte tatsächlich eine Weile dauern. Aber ja, normalerweise sind das meine Lieblingsrätsel. Auch wenn es nicht kurz ist, macht es Spaß zu beweisen, dass es möglich ist.
Cieric
@Matthew addierte den Bericht.
Cieric
Gibt es einen Grund, warum Sie sich für die Verwendung p[a.n]-=1von entschieden haben p[a.n]--?
Jonathan Frech
Eine solche Initialisierung qist keine zulässige Eingabemethode . Am häufigsten wird es entweder als Funktionsargument angegeben oder von stdin gelesen.
Ørjan Johansen