记一次算法面试题

前几天收到了某所AI算法岗的面试邀请,被要求24小时内做完4道编程题,可以上网搜、找人帮忙,提交可执行代码和解题思路。除了参加比赛和当年研究生复试,就再也没被要求过写算法代码了,好吧反正解题也好玩,就写呗。晚上九点才拿到题,大致扫了一眼,前三题都不难,第一题考察位运算、第二题贪心算法、第三题类似动态规划也不难,不到12点就搞定了外加解题文档,第四题是按规则生成给定的序列,看着也还行,需要用回溯算法解决,但是一旦考虑细节就发现有坑了,最后到第二天上午才全部搞定。只记录一下第四题吧。
记序列:1 4 1 5 6 7 4 2 3 5 2 6 3 7为S[7]
序列规则如下:数字n与n之间间隔n个数字,即:

  • 1与1之间间隔1个数字;
  • 3与3之间间隔1个数字;
  • 7与7之间间隔1个数字;
    以此类推。求S[12]
  • 规则很简单,但是生成的时候就会发现,s[i]到底应该填几合适呢?既然不知道那就只能猜了,先随便填一个按规则生成下去,当没法继续填数字的时候,就证明前面填错了,回溯算法呼之欲出咯。整体指导思想有了,怎么写代码呢?最初的想法很朴素,就直接向一维数组s[]里依次填数字啊,但是在回溯的时候发现不太容易实现,当前要填的数字即和该位置之前的状态有关,又和其余的位置数字有关,有点复杂不想继续纠缠下去了太痛苦。既然这样,就还是用空间换时间吧,设有二维数组A[2*n+1, n+1],行和列都加了1是因为想统一下标和数值,就浪费一行一列吧。行数为S的个数,列数为N,A的取值为0或1。如果A[i,j]=1,则表明s[i]=j,这样定义后,矩阵A每列至多两个1,每行一个1,且有A[i, j]=A[i + j + 1, j] = 1。每次回溯的时候,将该列置0即可,然后找个堆栈记录上次填充的是哪一行,便于回溯。源码:
    def friend_seq(N):
        # 为了统一下标和数值,舍弃第一行和第一列不使用
        # 矩阵work_matrix行数索引代表序列S的下标,列索引代表S在此处的取值
        # 矩阵work_matrix每行只有一个值为1,即A[i,j]=1,有:序列s[i]=j
        # 根据规则生成时有A[i, j]=A[i + j + 1, j] = 1,i,j都从1开始计数
        work_matrix = np.zeros((2 * N + 1, N + 1), dtype=np.int)
        work_matrix[2 * N, N] = work_matrix[2 * N - N - 1, N] = 1
        col_sum_vec = np.ones(N) * 2
        i = j = 1
        fill_row_index = []
        while i < 2 * N + 1:
            if np.sum(work_matrix[i, :]) == 0:
                while j < N + 1:
                    if i + j + 1 < 2 * N + 1 and np.sum(work_matrix[i + j + 1, :]) == 0 and np.sum(work_matrix[:, j]) < 2:
                        work_matrix[i, j] = work_matrix[i + j + 1, j] = 1
                        fill_row_index.append(i)
                        j = np.argmin(np.sum(work_matrix[1:, 1:], axis=0)) + 1
                        break
                    else:
                        j += 1
                if j == N + 1 and np.sum(np.sum(work_matrix[1:, 1:], axis=0) - col_sum_vec) != 0:
                    # 列已经遍历完,没有可用项,回溯
                    temp_i = fill_row_index[-1]
                    temp_j = np.argmax(work_matrix[temp_i, :])
                    # 更新i和j
                    i = np.argmax(work_matrix[:, temp_j])
                    j = temp_j + 1
                    # 将上一次的赋值清0
                    work_matrix[:, temp_j] = 0
                    fill_row_index.pop()
                else:
                    i += 1
            else:
                i += 1

        s = np.argmax(work_matrix[1:, 1:], axis=1) + 1
        print('{0} friend sequence: {1}'.format(N, s))

最后生成的序列:

7 friend sequence:  [1 4 1 5 6 7 4 2 3 5 2 6 3 7]
12 friend sequence: [ 1  2  1  3  2  8  9  3 10 11 12  5  7  4  8  6  9  5  4 10  7 11  6 12]

后记:将矩阵的最后一列初始化

work_matrix[2 * N, N] = work_matrix[2 * N - N - 1, N] = 1

可以提高大概10倍的效率,相当于做了一次剪枝。我猜测可能不是所有的数字N都有满足以上规则的序列,没测试过,还得继续搬砖啊!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章