简化火车套装


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],两个2x2元素在网格中心重叠?
Arnauld

是的,这是该测试用例的预期解决方案。
马修(马修)

我不清楚自相交的工作方式。您能否更明确地定义允许什么和禁止什么?
Wheat

Answers:


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的功能,但是当然删除了这些功能以节省至少一些空间。


我印象深刻-我对这个放弃了一点希望!会对写文章感兴趣
马修(Matthew)

@Matthew,我一有空就写出来。实际上可能要花一点时间。但是,是的,通常这些是我最喜欢做的难题。即使时间不短,证明它是可能也是很有趣的。
Cieric '18 -10-18

@Matthew添加了文章。
Cieric '18 -10-19

您选择使用p[a.n]-=1而不是有原因p[a.n]--吗?
乔纳森·弗雷希

那样的初始化q不允许的输入法。最常见的是,将其设为函数参数或从stdin中读取。
与Orjan约翰森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.