【问题标题】:4 by 3 lock pattern4 x 3 锁定模式
【发布时间】:2014-04-16 15:06:38
【问题描述】:

我遇到了这个problem

它要求计算特定长度的锁定模式可以在 4x3 网格中制作并遵循规则的方式数。 可能有一些点不能包含在路径中

有效的模式具有以下属性:

  • 一个模式可以用它第一次接触的点序列来表示(按照绘制模式的相同顺序),从 (1,1) 到 (2,2) 的模式是与从 (2,2) 到 (1,1) 的模式不同。

  • 对于模式表示中每两个连续的点 A 和 B,如果连接 A 和 B 的线段经过其他点,这些点也必须在序列中并且在 A 和 B 之前,否则模式将无效。例如,以 (3,1) 开头然后 (1,3) 的模式表示是无效的,因为该段通过 (2,2) 没有出现在 (3,1) 之前的模式表示中,并且正确这种模式的表示是 (3,1) (2,2) (1,3)。但是模式 (2,2) (3,2) (3,1) (1,3) 是有效的,因为 (2,2) 出现在 (3,1) 之前。

  • 在模式表示中,我们不会多次提及同一个点,即使模式将通过另一个有效段再次接触该点,并且模式中的每个段必须从一个点到另一个点图案之前没有接触到的点,它可能会穿过一些已经出现在图案中的点。

  • 模式的长度是模式表示中每两个连续点之间的曼哈顿距离之和。两点 (X1, Y1) 和 (X2, Y2) 之间的曼哈顿距离为 |X1 - X2| + |Y1 - Y2| (其中 |X| 表示 X 的绝对值)。

  • 图案必须至少接触两个点

我的方法是蛮力,遍历点,从点开始并使用递归递减长度直到长度为零,然后将组合数加 1。

有没有办法用数学方程式计算它,或者有更好的算法?

更新: 这是我所做的,它给出了一些错误的答案!我认为问题出在isOk 函数中! notAllowed 是不允许点的全局位掩码。

bool isOk(int i, int j, int di,int dj, ll visited){
    int mini = (i<di)?i:di;
    int minj = (j<dj)?j:dj;

    if(abs(i-di) == 2 && abs(j-dj) == 2 && !getbit(visited, mini+1, minj+1) )
        return false;
    if(di == i && abs(j - dj) == 2 && !getbit(visited, i,minj+1) )
        return false;
    if(di == i && abs(j-dj) == 3 && (!getbit(visited, i,1) || !getbit(visited, i,2)) )
        return false;
    if(dj == j && abs(i - di) == 2 && !getbit(visited, 1,j) )
        return false;

    return true;
}

int f(int i, int j, ll visited, int l){
    if(l > L) return 0;
    short& res = dp[i][j][visited][l];
    if(res != -1) return res;
    res = 0;
    if(l == L) return ++res;

    for(int di=0 ; di<gN ; ++di){
        for(int dj=0 ; dj<gM ; ++dj){
            if( getbit(notAllowed, di, dj) || getbit(visited, di, dj) || !isOk(i,j, di,dj,  visited) )
                continue;
            res += f(di, dj, setbit(visited, di, dj), l+dist(i,j , di,dj));
        }
    }
    return res;
}

【问题讨论】:

    标签: c++ algorithm combinations permutation


    【解决方案1】:

    My answer to another question也可以适应这个问题。

    f(i,j,visited,k) 完成一个部分模式的方法数,当我们当前在节点 (i,j) 时,已经访问过设置 visited 并且到目前为止已经走了 k 的路径长度。我们可以将 visited 表示为位掩码。

    我们可以通过尝试所有可能的下一步动作递归地计算 f(i,j,visited,k),并应用 DP 来重用子问题解决方案:

    f(i,j, 已访问, L) = 1

    f(i,j,visited,k) = 0 如果 k > L

    f(i,j,visited,k) = sum(可能的移动 (i',j'): f(i',j',visited UNION {(i',j')},k + dis( (i,j), (i',j')))

    可能的移动是那些跨越多个已访问顶点然后以未访问(且未被禁止)的顶点结束的移动。

    如果D是禁止顶点的集合,那么问题的答案是

    sum((i,j) 不在 D 中:f(i,j, {(i,j)}, L))。

    运行时间类似于 O(X^2 * Y^2 * 2^(X*Y) * 最大可能长度)。我猜最大可能的长度实际上远低于 1000

    更新:我实施了这个解决方案,它被接受了。我用以下方式列举了可能的移动:假设我们在点 (i,j) 并且已经访问了顶点集合visited。枚举所有不同的互质数对 (dx,dy) 0 dx, j + kdy) 找到最小的 k仍然是一个有效的网格点,并且 P_k 不在 visited 中。如果 P_k 没有被禁止,则它是一个有效的移动。

    最大可能的路径长度为 39。

    我正在使用大小为 3 * 4 * 2^12 * 40 的 DP 数组来存储子问题结果。

    【讨论】:

    • 能否详细解释一下算法,或者提供伪代码?
    • @Rami 另一个答案包含实际的 C++ 代码。唯一的区别是长度参数
    • Niklas B,我还在学习 DP,我已经尝试针对这个问题调整算法但我做不到,你能帮帮我吗?
    • @RamiJarrar 我可以回答问题并澄清我的答案,是的。所以请提前询问(但请不要询问代码)。首先在位掩码上使用 DP 执行一些更简单的任务不会有什么坏处
    • 好的,你能解释一下 f(i,j,visited, L) 背后的算法吗?
    【解决方案2】:

    组合的几个属性可用于优化蛮力方法:

    1. 使用镜像(水平、垂直或两者),您可以为找到的每个组合生成 4 种组合(水平或垂直线除外)。也许您可以只考虑从一个象限开始的组合。

    2. 您通常可以通过平移(移动组合)生成相同长度的其他组合。

    【讨论】:

    • 好的,但是路径中可能没有包含一些点,这打破了镜像?你能解释更多吗!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-12
    • 2012-08-16
    • 2014-04-04
    • 1970-01-01
    • 2023-03-29
    • 2018-04-26
    • 2011-03-30
    相关资源
    最近更新 更多