Упрощенный поезд


27

Существует множество различных типов комплектов поездов, начиная с деревянных гусениц, таких как Brio, и заканчивая полностью цифровым управлением идеальными крошечными металлическими копиями реальных поездов, но все они требуют разработки гусеницы, в идеале с использованием как можно большего количества деталей.

Итак, ваша задача состоит в том, чтобы определить, возможно ли при заданном вводе доступных частей построить полную замкнутую цепь, используя все элементы, и, если нет, сколько частей останется от максимально возможной цепи.

Так как это упрощенный набор поездов, есть только 3 элемента: большая кривая, маленькая кривая и прямая. Все они основаны на квадратной сетке:

Квадратная сетка, показывающая большую кривую и маленькую кривую

  • «Большая кривая» - это угол 90 градусов, охватывающий 2 единицы в каждом измерении
  • «Маленькая кривая» - это угол 90 градусов, охватывающий одну единицу в каждом направлении
  • «Прямой» - прямой элемент длиной 1 единица

Это означает, что минимально возможная цепь состоит из 4 маленьких кривых - это круг, радиус 1 единица. Это можно расширить, добавив пары прямых элементов для формирования различных овалов. Существуют и другие схемы, которые можно добавить, добавив больше кривых или смешав типы кривых.

Этот набор поездов не включает в себя какие-либо перекрестки или методы пересечения путей, поэтому недопустимо, чтобы два элемента соединялись с одним и тем же концом другого элемента (без Y-образований) или для пересечения друг с другом (без X-образований) , Кроме того, это набор поездов, поэтому любая формация, которая не позволяет проходить поезду, недопустима: примеры включают прямые, встречающиеся под углами 90 градусов (между перпендикулярными прямыми всегда должна быть кривая) и кривые, встречающиеся под углами 90 градусов (кривые должны течь).

Вы также хотите использовать как можно больше частей, игнорируя их тип, поэтому вы всегда будете выбирать схему, в которой больше битов. Наконец, у вас есть только одна последовательность, поэтому любое решение, которое приводит к нескольким цепям, неприемлемо ,

вход

Либо массив из трех целых чисел, все больше или равный 0, соответствующий количеству доступных больших кривых, маленьких кривых и прямых линий, или параметры, переданные в вашу программу, в том же порядке.

Выход

Число, соответствующее количеству кусков, оставшихся при построении максимально возможной схемы для предоставленных элементов.

Тестовые данные

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

Заметки

  • 2 прямые и небольшая кривая эквивалентны большой кривой, но используют больше фигур, поэтому предпочтительны - никогда не должно быть ситуации, когда остается эта комбинация, если в схеме есть большие кривые
  • 4 маленьких кривых обычно можно поменять местами на 4 прямые, но только если это не приведет к пересечению контура
  • Набор поездов также идеализирован - элементы пути занимают указанную ширину, поэтому в некоторых случаях допустимо, чтобы кривые проходили через один квадрат сетки, не пересекаясь. Сетка просто определяет размеры элемента. В частности, две большие кривые могут быть размещены так, чтобы квадрат сетки в верхнем левом углу примера диаграммы также был нижним правым квадратом другой большой кривой, идущей слева направо (с диаграммой, показывающей одну, идущую справа налево)
  • Маленькая кривая может поместиться в пустом пространстве под большой кривой (справа внизу квадрат сетки). Вторая большая кривая также могла бы использовать это пространство, сдвинув одну поперек и одну вниз от первой
  • Маленькая кривая не может поместиться в том же пространстве сетки, что и внешняя сторона большой кривой - в основном потому, что нет способа подключиться к ней, который не пересекается незаконно

Так что выход для [5,0,0]или [0,5,0]будет 1. Это верно? Не могли бы вы добавить такой тест?
Arnauld

@arnauld Да, это правильно. Всегда должно быть оставшееся количество элементов после построения максимально длинной цепи.
Матфея

Не могли бы вы подтвердить, что это решение для [8,0,0]двух элементов 2х2, перекрывающихся в центре сетки?
Arnauld

Да, это ожидаемое решение для этого теста.
Матфея

Мне не ясно, как работает самопересечение. Не могли бы вы более четко определить, что разрешено, а что запрещено?
Пшеничный волшебник

Ответы:


9

[JavaScript (Node.js)], 1220 байт

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)};

Попробуйте онлайн!

Примечание. На самом деле входом является переменная q в начале. [2,6,4] также займет немного времени, так как это грубое решение без оптимизации.

Я действительно сделал это, потому что на него не было ответа больше года, и мне было просто любопытно, возможно ли это.


Оригинальный код:

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);

Сначала я должен включить изображение плитки, которую я использовал.

использованные плитки

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.

Извините, если описание трудно читать, я не привык объяснять, как работает мой код.

PS Я также сделал несколько функций для рисования карт в png, но, конечно, они были удалены, чтобы сэкономить хотя бы немного места.


Я впечатлен - я бы потерял надежду на это! Было бы интересно написать
Мэтью

@ Матфе, я посмотрю, когда у меня будет время, чтобы написать. На самом деле это может занять некоторое время. Но да, обычно это мои любимые головоломки. Даже если он не короткий, весело доказать, что это возможно.
Cieric

@ Мэтью добавил запись.
Cieric

Есть ли причина, по которой вы решили использовать p[a.n]-=1вместо p[a.n]--?
Джонатан Фрех

Подобная инициализация qне является допустимым методом ввода . Чаще всего, либо сделайте это аргументом функции, либо прочитайте его из стандартного ввода.
Орджан Йохансен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.