前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用平衡三进制巧解算法题

使用平衡三进制巧解算法题

作者头像
我不是码神
发布2022-07-18 11:59:02
2520
发布2022-07-18 11:59:02
举报
文章被收录于专栏:流媒体技术

现有一个 3x3 规格的 Android 智能手机锁屏程序和两个正整数 m 和 n ,请计算出使用最少m 个键和最多 n个键可以解锁该屏幕的所有有效模式总数。 其中有效模式是指: 1、每个模式必须连接至少m个键和最多n个键; 2、所有的键都必须是不同的; 3、如果在模式中连接两个连续键的行通过任何其他键,则其他键必须在模式中选择,不允许跳过非选择键(如图); 4、顺序相关,单键有效(这里可能跟部分手机不同)。 输入:m,n 代表允许解锁的最少m个键和最多n个键 输出:满足m和n个键数的所有有效模式的总数

正常解题思路是把九个点按如下编号进行计算

 \[       \begin{bmatrix}        0 & 1 &2\\3&4&5\\6&7&8        \end{bmatrix}     \]
\[ \begin{bmatrix} 0 & 1 &2\\3&4&5\\6&7&8 \end{bmatrix} \]

这种编号虽然比较直观,但是缺失去了九个点的对称性。

那么如何给9个点编号,以示区分又体现出对称性呢?可以用坐标方式,将中间的点标记为0,0原点:

 \[       \begin{bmatrix}        [-1,1] & [0,1] &[1,1] \\  [-1,0]&[0,0]&[1,0]\\ [-1,-1]&[0,-1]&[1,-1]        \end{bmatrix}     \]
\[ \begin{bmatrix} [-1,1] & [0,1] &[1,1] \\ [-1,0]&[0,0]&[1,0]\\ [-1,-1]&[0,-1]&[1,-1] \end{bmatrix} \]

但这样会给编程带来更多的复杂性,这时候我们可以引入平衡三进制,将坐标改为如下的数字:

 \[       \begin{bmatrix}        T1 & 01 &11 \\  T0&00&10\\ TT&0T&1T        \end{bmatrix}     \]
\[ \begin{bmatrix} T1 & 01 &11 \\ T0&00&10\\ TT&0T&1T \end{bmatrix} \]

再进一步转换成十进制数字:

 \[       \begin{bmatrix}        -2 & 1 &4 \\  -3&0&3\\ -4&-1&2        \end{bmatrix}     \]
\[ \begin{bmatrix} -2 & 1 &4 \\ -3&0&3\\ -4&-1&2 \end{bmatrix} \]

现在9个点就变成这个矩阵了,从题目中看到,我们需要计算两点之间是否存在一个点,用这个矩阵中的数字就很好算了,两个数字相加=0说明穿过中间的点。如果两个点相乘的绝对值=8说明是2、4组合(包括-2、-4)必经过(1、3、-1、-3),直接通过相加除以2即能得到穿过的点,比如4和2,相加除以2得到3,即4和2必然穿过3,以此类推。

最后通过类似寻路算法来遍历所有的路径,这里有一个技巧,就是不需要遍历所有的起点,起点在角落的四个点-2、4、-4、2可以形成相同的形状(通过旋转)所以只需要计算任意一个角的路径数X4即可,1、3、-1、-3同理也是X4。最后,从0点出发,0-1作为起点X4,0-4作为起点X4即可。

实现代码如下:

代码语言:javascript
复制
/**
  * 实现方案
  * @param m int整型 最少m个键
  * @param n int整型 最多n个键
  * @return int整型
  */
const dots = {};//根据坐标速查map
//构造坐标系,0,0为原点
[-1, 0, 1].forEach((i, index, cor) => (cor.forEach(j => dots['' + (i * 3 + j)] = (i * 3 + j))))
function solution(m, n) {
    //存储长度对应对组合数
    const counts = [0].concat(new Array(n).fill(0))
    //移动到下一个节点 path:已有路径,start:本次起点,_remain:剩余可用节点
    function moveNext(path, start, _remain) {
        const { ['' + start]: _, ...remain } = _remain
        //拼入路径
        path = [...path, start]
        const len = path.length + 1
        //计算出可到达的节点
        const reach = Object.keys(remain).map(k => remain[k]).filter(i => (Math.abs(i * start) == 8 || i + start == 0) ? path.includes((i + start) / 2) : true)
        //由于中心对称性,每一种方案数量X4
        counts[len] += reach.length * 4
        if (len < n) reach.forEach(i => moveNext(path, i, remain))
    }
    if (n > 0) {
        //得到排除中心点的集合
        const { ['0']: dot00, ...dotsWithout00 } = dots
        //计算任意顶点和边节点的集合数,以及起点为中心点,第二节点为顶点或者边节点的集合数
        moveNext([], 1, dots)
        moveNext([], 4, dots)
        moveNext([0], 1, dotsWithout00)
        moveNext([0], 4, dotsWithout00)
        counts[1] = 9
        if (n > 1) counts[2] += 8//弥补起点为中心点路径长度为2的8种方案数
    }
    console.log(counts)
    return counts.reduce((acc, c, i) => i >= m ? acc + (c || 0) : 0)
}
solution(3, 3)
module.exports = {
    solution: solution
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-10-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档