今天是元旦放假第一天,想着多睡会,不想改论文了。赖床的时候刷了刷抖音,发现有很多视频反复出现这么一个题目。声称是一道农民工出的题目,难倒了几万清华北大的高材生。
说实话,这些年来这种UC式的标题让人很疲劳,标题反正是一个比一个夸张。我仔细看了看题目,分析了一下。题目如下所示。
首先声明一下我的结论,那就是这个“农民大伯”出的题目不光清华北大的高材生一笔画不出来,欧拉莱布尼茨掀开棺材板出来也是无能为力。
这个题目实际上就是图论里寻找到一条起点到终点的哈密尔顿路径的问题。哈密尔顿路径是在1859年,由爱尔兰数学家哈密尔顿提出的周游世界游戏中的一个数学概念。其定义就是在一个图中,遍历每个顶点一次且仅一次的路径称为哈密尔顿路径。
我查阅了一下哈密尔顿路径判定的必要条件,发现似乎都不能应用到这个题目上来。要从数学证明上来解决对我这种图论已经忘的差不离的菜鸡来说还是有点难度。所以我就借助网上的遍历穷举哈密尔顿路径的方法对于这个问题进行分析,解决。
首先我先将每个格子看成一个顶点,由于图里不能有斜线所以我们的问题就是在下面这个图中求取一条从顶点1到顶点13的哈密尔顿路径(通过其他所有顶点并且只能通过一次)。
把这个图用邻接矩阵表示出来(18个点就是18*18的矩阵,还是有点大噻)
然后用https://blog.csdn.net/zhangyifei521/article/details/53283028里的Java源码,把里头的邻接矩阵X换成上面那个,源码如下所示。
package tiktok; public class hamilton { public static void main(String[] args) { hamilton obj = new hamilton(); int[][] x = { {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0} }; // int[][] y = { { 0, 1, 0, 0, 0, 1 }, // { 1, 0, 1, 0, 0, 1 }, // { 0, 1, 0, 1, 1, 0 }, // { 0, 0, 1, 0, 0, 0 }, // { 0, 0, 1, 0, 0, 1 }, // { 1, 1, 0, 0, 1, 0 } }; // // int[][] z = { { 0, 1, 1, 0, 0, 1 }, { 1, 0, 1, 0, 0, 0 }, // { 1, 1, 0, 1, 0, 1 }, { 0, 0, 1, 0, 1, 0 }, // { 0, 0, 0, 1, 0, 1 }, { 1, 0, 1, 0, 1, 0 } }; obj.allHamiltonPath(x); // list all Hamiltonian paths of graph // obj.HamiltonPath(z,1); //list all Hamiltonian paths start at point 1 } static int len; static int[] path; static int count = 0; public void allHamiltonPath(int[][] x) { // List all possible Hamilton path // in the graph len = x.length; path = new int[len]; int i; for (i = 0; i < len; i++) { // Go through column(of matrix) path[0] = i + 1; findHamiltonpath(x, 0, i, 0); } } // public void HamiltonPath(int[][] x, int start) { // List all possible // // Hamilton path with // // fixed starting point // len = x.length; // path = new int[len]; // int i; // for (i = start - 1; i < start; i++) { // Go through row(with given // // column) // path[0] = i + 1; // findHamiltonpath(x, 0, i, 0); // } // } private void findHamiltonpath(int[][] M, int x, int y, int l) { int i; for (i = x; i < len; i++) { // Go through row if (M[i][y] != 0) { // 2 point connect if (detect(path, i + 1))// if detect a point that already in the // path => duplicate continue; l++; // Increase path length due to 1 new point is connected path[l] = i + 1; // correspond to the array that start at 0, // graph that start at point 1 if (l == len - 1) {// Except initial point already count // =>success connect all point count++; if (count == 1) System.out.println("Hamilton path of graph: "); display(path); l--; continue; } M[i][y] = M[y][i] = 0; // remove the path that has been get and findHamiltonpath(M, 0, i, l); // recursively start to find new // path at new end point l--; // reduce path length due to the failure to find new path M[i][y] = M[y][i] = 1; // and tranform back to the inital form // of adjacent matrix(graph) } } path[l + 1] = 0; // disconnect two point correspond the failure to find // the.. } // possible hamilton path at new point(ignore newest point try another // one) public void display(int[] x) { System.out.print(count + " : "); for (int i : x) { System.out.print(i + " "); } System.out.println(); } private boolean detect(int[] x, int target) { // Detect duplicate point in // Halmilton path boolean t = false; for (int i : x) { if (i == target) { t = true; break; } } return t; } }
运行结果会把所有的哈密尔顿路径(不限制起点终点)都打印出来,一共有672条哈密尔顿路径。
但是如果将起点限制为顶点1,终点限制为顶点13,那么没有一条哈密尔顿路径可以满足此条件。(起点为1的哈密尔顿路径有78条,但是没有一条终点为13)
所以,通过穷举遍历的方法,这道农民大伯出的题目是无解的。当然各位抖音上的大神通过折纸,画到外面的方式来增加/减少图中的顶点,也许确实可以找到这样一条路径,但是可能也违背了农民大伯的初衷了吧。
另外,如果有大佬知道怎么数学证明这个问题的话,也欢迎在评论里留言,大家一起交流学习。