Synteny Blocks Have Orientations

In “Enumerating Gene Orders”, we introduced synteny blocks for two different species, which are very similar areas of two species genomes that have been flipped and moved around by rearrangements. In that problem, we used the permutation to model the order of synteny blocks on a single chromosome.

However, each strand of a DNA molecule has an orientation (as RNA transcription only occurs in one direction), and so to more prudently model chromosomes using synteny blocks, we should provide each block with an orientation to indicate the strand on which it is located. Adding orientations to synteny blocks requires us to expand our notion of permutation so that each index in the permutation has its own orientation.

Problem

A signed permutation of length nn is some ordering of the positive integers {1,2,…,n} in which each integer is then provided with either a positive or negative sign (for the sake of simplicity, we omit the positive sign). For example, π=(5,−3,−2,1,4) is a signed permutation of length 5.
Given: A positive integer n≤6.
Return: The total number of signed permutations of length n, followed by a list of all such permutations (you may list the signed permutations in any order).

Sample Dataset

2

Sample Output

8
-1 -2
-1 2
1 -2
1 2
-2 -1
-2 1
2 -1
2 1

Solution 1

交换法全排列:与之前普通交换法唯一区别在于每个数字有 +- ,因此交换一个数字后,进行正数和负数选取。

  1. from typing import List
  2. class Solution:
  3. def orientedPermute(self, nums: List[int]) -> List[List[int]]:
  4. n = len(nums)
  5. res = []
  6. def backtrace(idx: int) -> None:
  7. if idx == n:
  8. res.append(nums[:])
  9. return
  10. for j in range(idx, n):
  11. nums[j], nums[idx] = nums[idx], nums[j]
  12. # 正数
  13. backtrace(idx + 1)
  14. # 负数
  15. nums[idx] *= -1
  16. backtrace(idx + 1)
  17. nums[idx] *= -1
  18. nums[j], nums[idx] = nums[idx], nums[j]
  19. backtrace(0)
  20. return res
  21. n = 2
  22. ret = Solution().orientedPermute(list(range(1, n + 1)))
  23. print(len(ret))
  24. for op in ret:
  25. print(' '.join(map(str, op)))

Solution 2

标记法全排列:

  1. 先不考虑负数,假设当前有 n 个数进行全排列,第一个位置有 n 种可能,第二个位置有 n-1 种可能;第 k 个位置有 n-k+1 种可能。
  2. 如何防止一个数被重复添加?例如 x 在第 k 位置之前已经被用过,那么第 k 位置上候选人一定没有 x 。(全排列不存在重复数字,若存在重复数字则标记元素下标即可)

如何处理负数呢?其实就是在当前选中数字 y 当作第 k 位置上的数后, 再额外进行一次 -y 当作第 k 位置上的数。

  1. from typing import List
  2. class Solution:
  3. def orientedPermute(self, nums: List[int]) -> List[List[int]]:
  4. n = len(nums)
  5. res = []
  6. path = []
  7. visited = [False] * n
  8. def backtrace() -> None:
  9. if len(path) == n:
  10. res.append(path[:])
  11. return
  12. for j in range(n):
  13. if not visited[j]: # 下标 j 的元素未访问过
  14. visited[j] = True # 正负都用过
  15. path.append(nums[j])
  16. backtrace()
  17. path[-1] *= -1
  18. backtrace()
  19. path.pop()
  20. visited[j] = False # 撤销访问
  21. backtrace()
  22. return res
  23. n = 2
  24. ret = Solution().orientedPermute(list(range(1, n + 1)))
  25. print(len(ret))
  26. for op in ret:
  27. print(' '.join(map(str, op)))