我有一个4x4格子的字母。如何计算从任意点到包含2到10个点的任何点的所有可能路线?
路线中的所有点必须垂直,水平或对角连接到同一路线中的另一个点。例如,您可以从A到B,A到E和A到F但不是A到C.
每个点只能在路线中使用一次。
以下是25种可能排列的示例:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
| E | F | G | H |
+---+---+---+---+
| I | J | K | L |
+---+---+---+---+
| M | N | O | P |
+---+---+---+---+
- AB
- ABC
- ABCD
- ABCDH
- ABCDHG
- ABCDHGF
- ABCDHGFE
- ABCDHGFEI
- ABCDHGFEIJ
- AE
- AEI
- AEIM
- AEIMN
- AEIMNJ
- AEIMNJF
- AIEMNJFB
- AIEMNJFBC
- AIEMNJFBCG
- AFKP
- PONM
- FGKL
- NJFB
- MNJGD
现在我应该清楚这个问题。我不是要问如何得到所有的排列。我问的是可能的排列总量(即一个整数)以及如何计算它。
答案 0 :(得分:2)
正如评论中所提到的那样,问题可以通过java中从左上角开始的基本DFS来回答(0,0)
编辑:我为约束
添加了if(count(visited)>10) return;
static int count=0;
static int count(boolean[][] b){
int r = 0;
for(int i=0;i<b.length;i++){
for(int j=0;j<b[0].length;j++){
if(b[i][j]) r++;
}
}
return r;
}
static boolean[][] copy(boolean[][] arr){
boolean [][] r = new boolean[arr.length][];
for(int i = 0; i < arr.length; i++)
r[i] = arr[i].clone();
return r;
}
static void dfs(int i, int j,boolean[][] visited) {
visited[i][j] = true;
if(count(visited)>10) return;
count++;
for (int k=-1;k<2;k++) {
for (int l=-1;l<2;l++) {
int r = i+k;
int c = j+l;
if (r>-1 && r<visited.length && c>-1 && c<visited.length && !visited[r][c]){
dfs(r,c,copy(visited));
}
}
}
}
public static void main(String args[]) {
boolean[][] visited = {
{false, false, false, false},
{false, false, false, false},
{false, false, false, false},
{false, false, false, false}
};
// dfs(row,column,initialize all to false)
dfs(0,0,visited);
System.out.println(count-1);
}
以上脚本只是遍历每个排列并且每次都会递增count
,因为这包括我在底部(0,0)
count-1
)
输出:105837
(根据我错误的原始1012519
编辑)
对于2x2从同一个地方开始,我得到15
。您可以在运行中看到
static int count=0;
static int count(boolean[][] b){
int r = 0;
for(int i=0;i<b.length;i++){
for(int j=0;j<b[0].length;j++){
if(b[i][j]) r++;
}
}
return r;
}
static boolean[][] copy(boolean[][] arr){
boolean [][] r = new boolean[arr.length][];
for(int i = 0; i < arr.length; i++)
r[i] = arr[i].clone();
return r;
}
static void dfs(int i, int j,boolean[][] visited,String str) {
visited[i][j] = true;
if (count(visited)>10) return;
count++;
str+="("+i+","+j+")";
System.out.println(str+": "+count);
for (int k=-1;k<2;k++) {
for (int l=-1;l<2;l++) {
int r = i+k;
int c = j+l;
if (r>-1 && r<visited.length && c>-1 && c<visited.length && !visited[r][c]){
dfs(r,c,copy(visited),str);
}
}
}
}
public static void main(String args[]) {
boolean[][] visited = {
{false, false},
{false, false}
};
dfs(0,0,visited,"");
// "count-1" to account for the starting position
System.out.println(count-1);
}
输出:
(0,0): 1
(0,0)(0,1): 2
(0,0)(0,1)(1,0): 3
(0,0)(0,1)(1,0)(1,1): 4
(0,0)(0,1)(1,1): 5
(0,0)(0,1)(1,1)(1,0): 6
(0,0)(1,0): 7
(0,0)(1,0)(0,1): 8
(0,0)(1,0)(0,1)(1,1): 9
(0,0)(1,0)(1,1): 10
(0,0)(1,0)(1,1)(0,1): 11
(0,0)(1,1): 12
(0,0)(1,1)(0,1): 13
(0,0)(1,1)(0,1)(1,0): 14
(0,0)(1,1)(1,0): 15
(0,0)(1,1)(1,0)(0,1): 16
15
与4x4相同的脚本而不是最后6行输出:
(0,0)(1,1)(2,2)(3,3)(3,2)(3,1)(3,0)(2,1)(1,2)(0,3): 105834
(0,0)(1,1)(2,2)(3,3)(3,2)(3,1)(3,0)(2,1)(1,2)(1,3): 105835
(0,0)(1,1)(2,2)(3,3)(3,2)(3,1)(3,0)(2,1)(1,2)(2,3): 105836
(0,0)(1,1)(2,2)(3,3)(3,2)(3,1)(3,0)(2,1)(2,0): 105837
(0,0)(1,1)(2,2)(3,3)(3,2)(3,1)(3,0)(2,1)(2,0)(1,0): 105838
105837
答案 1 :(得分:0)
你的问题的要求很复杂,我怀疑有一个简单的数学计算 - 至少我想不到一个。这是递归的Python代码,用于查找路径计数。
SIDE = 4 # Length of side of grid
MAXLEN = 10 # Maximum path length allowed
SIDE2 = SIDE + 2
DIRS = ( # offsets for directions
-1 * SIDE2 - 1, # up & left
-1 * SIDE2 + 0, # up
-1 * SIDE2 + 1, # up & right
0 * SIDE2 - 1, # left
0 * SIDE2 + 1, # right
1 * SIDE2 - 1, # down & left
1 * SIDE2 + 0, # down
1 * SIDE2 + 1, # down & right
)
def countpaths(loc, pathlen):
"""Return the number of paths starting at the point indicated by
parameter loc of length at most parameter pathlen, not repeating
points or using points marked False in global variable isfree[]."""
global isfree
pathcnt = 1 # count sub-path of just this one point
if pathlen > 1:
isfree[loc] = False
for dir in DIRS:
if isfree[loc + dir]:
pathcnt += countpaths(loc + dir, pathlen - 1)
isfree[loc] = True
return pathcnt
# Init global boolean array variable to flag which points are still available
isfree = [1 <= r <= SIDE and 1 <= c <= SIDE
for r in range(SIDE2) for c in range(SIDE2)]
# Use the symmetries of the square grid to find count of paths in grid
allpathcnt = 0
for r in range(1, (SIDE + 1) // 2 + 1): # do a triangular slice of the grid
for c in range(1, r + 1):
# Find the number of similar (by symmetry) points in the grid
if 2 * r - 1 == SIDE:
if r == c:
sym = 1 # center of entire grid
else:
sym = 4 # center of column
else:
if r == c:
sym = 4 # diagonal
else:
sym = 8 # other
# Add paths starting at this kind of point removing those of length 1
allpathcnt += sym * (countpaths(r * SIDE2 + c, MAXLEN) - 1)
print('Total path count is ' + str(allpathcnt))
此代码通过将路径长度限制为10并删除长度为1的路径来考虑路径长度在2到10之间的要求。使用数组isfree[]
可以满足不重复点的要求注意哪些点仍然是免费的(True
)以及哪些点已经使用或不应该使用(False
)。
Python是一种有点慢的语言,所以我通过从内部递归中移出一些计算来提高速度。我在4x4网格周围使用了一个始终为假点的边界边框,无需进行显式边界检查。我使用了一维列表而不是二维列表,并将常数DIRS
中的每个单元格的偏移量预编码为相邻单元格(用于“方向”)。我通过不使用所有16个起点来进行最终优化。有4个角点,如A,8个侧点,如B,4个中心点,如F,所以我只是找到了A,B和F的路径数,并计算了从所有点开始的总数。 / p>
此版本的代码可以处理任何大小的方格和最大路径长度。我通过将SIDE
和MAXLEN
分别改为1,2和3来检查我的代码,并手动检查每个点的结果。
我得到的最终答案是
1626144
我有兴趣注意到占用空间最多的代码部分是决定网格中某个点对称性的部分。我已经找到了其他更简洁的方法,但它们的可读性要低得多。