什么是哈密尔顿回路/路径?

一:哈密尔顿回路与哈密尔顿路径

1859 年,爱尔兰数学家哈密尔顿(Hamilton)提出了一个“周游世界”的游戏:

在一个正十二面体的二十个顶点上,标注了伦敦,巴黎,莫斯科等世界著名大城市,正十二面体的棱表示连接着这些城市的路线。要求游戏参与者从某个城市出发,把所有的城市都走过一次,且仅走过一次,然后回到出发点。

简而言之,哈密尔顿回路是指,从图中的一个顶点出发,沿着边行走,经过图的每个顶点,且每个顶点仅访问一次,之后再回到起始点的一条路径。如上图所示,我们的起始点选定为 Washington DC,灰色实线构成的一条路径就是一条哈密尔顿回路。

在图论算法的领域中,哈密尔顿回路(Hamilton Loop)和路径(Hamilton Path)在定义上是有所区分的:

  • 哈密尔顿回路(Hamilton Loop)要求从起始点出发并能回到起始点,其路径是一个环。

  • 哈密尔顿路径(Hamilton Path)并不要求从起始点出发能够回到起始点,也就是说:起始顶点和终止顶点之间不要求有一条边。

譬如上面这两个图,左图既存在哈密尔顿回路,也存在哈密尔顿路径。而右图只存在哈密尔顿路径,并不存在哈密尔顿回路。

二:求解哈密尔顿回路

如何求解一个图是否存在哈密尔顿回路呢?

一个最直观的想法就是暴力求解。暴力求解的思路也很简单:我们遍历图的每一个顶点 v,然后从顶点 v 出发,看是否能够找到一条哈密尔顿回路。

暴力求解的代价同求解全排列问题是等价的,其时间复杂度为 O ( N ! ) O(N!) O(N!),N 为图的顶点的个数。

O ( N ! ) O(N!) O(N!) 是一个非常高的复杂度,它并不是一个多项式级别的复杂度。像 O ( 1 ) O(1) O(1) O ( N l o g N ) O(NlogN) O(NlogN) O ( N 2 ) O(N^2) O(N2) 这些我们常见的复杂度都是多项式级的复杂度,而 O ( a N ) O(a^N) O(aN) O ( N ! ) O(N!) O(N!) 这些复杂度是非多项式级的,也就是说,在数据量 N 极大的情况下,我们的现代计算机是不能承受的。

那么除了暴力求解哈密尔顿回路问题,是否存在更好的算法?

很遗憾的是,对于哈密尔顿问题,目前并没有多项式级别的算法。我们只能在暴力破解的基础上,尽量去做到更多的优化,譬如回溯剪枝,记忆化搜索等,但是,还没有找到一种多项式级别的算法来解决哈密尔顿问题。

通常,这类问题也被称为 NP(Non-deterministic Polynomial)难问题。

综上所述,求解哈密尔顿回路,我们可以采用回溯+剪枝的思想来进行求解。

对于这样一个图:

回溯+剪枝的过程模拟如下:

Java 代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HamiltonLoop {
    private Graph G;
    private boolean[] visited;
    private int[] pre;
    private int end; // 用来表示最后一个被遍历的顶点

    public HamiltonLoop(Graph G) {
        this.G = G;
        visited = new boolean[G.V()];
        pre = new int[G.V()];
        end = -1;
        dfs(0, 0, G.V());
    }

    /**
     * 对图进行深度优先遍历
     *
     * @param v
     * @param parent
     * @param left   表示还有多少个点没有遍历
     * @return
     */
    private boolean dfs(int v, int parent, int left) {
        visited[v] = true;
        pre[v] = parent;
        left--;
        
        // 如果所有的点遍历完毕,并且起始点和终止点存在一条边
        if (left == 0 && G.hasEdge(0, v)) {
            end = v;
            return true;
        }
        // G.adj(v) 为寻找 v 的相邻顶点
        for (int w : G.adj(v))
            if (!visited[w]) {
                if (dfs(w, v, left)) return true;
            }

        visited[v] = false;
        return false;
    }

    /**
     * 获取哈密尔顿回路
     *
     * @return
     */
    public List<Integer> result() {
        List<Integer> res = new ArrayList<>();
        if (end == -1) return res;
        int cur = end;
        while (cur != 0) {
            res.add(cur);
            cur = pre[cur];
        }
        res.add(0);
        Collections.reverse(res);
        return res;
    }
}

对于这两个图进行测试,我的 HamiltonLoop 算法类输出的结果如下:

图1

[0, 1, 2, 3]

图2

[]

因为图2 本身就不构成一个哈密尔顿回路,所以,其结果输出为空也符合我们的意料之中。

三:求解哈密尔顿路径

求解哈密尔顿路径和求解哈密尔顿回路的算法整体框架是基本一致的。

对于求解哈密尔顿路径来说,起始点是谁很重要,同一个图,从有的起始点出发就存在哈密尔顿路径,从有的起始点出发就不存在哈密尔顿路径。所以,我们在算法设计中,构造函数需要用户显示地传入起始点。

我的求解哈密尔顿路径的算法类 HamiltonPath 的构造函数是这样的:

// 用户需要在构造器中传入图 G 以及起始点 s
public HamiltonPath(Graph G,int s){
    // ...    
}

除了这一点外,求解哈密尔顿路径只需要保证,从起始点开始,所有的点均被遍历到且仅被遍历一次,并不需要起始点和终止点之间有边。所以,在 dfs 的逻辑中,我们只需要改变递归的终止条件即可:

// 不需要保证终止点 v 和 起始点 s 存在一条边,即: G.hasEdge(v, s)
if(left == 0 /*&& G.hasEdge(v, s)*/){
	   end = v;
    return true;
}

整体的 Java 代码如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HamiltonPath {
    private Graph G;
    private int s;
    private boolean[] visited;
    private int[] pre;
    private int end;
    private int left;

    public HamiltonPath(Graph G, int s) {
        this.G = G;
        this.s = s;
        visited = new boolean[G.V()];
        pre = new int[G.V()];
        end = -1;
        this.left = G.V();

        dfs(s, s);
    }

    private boolean dfs(int v, int parent) {
        visited[v] = true;
        pre[v] = parent;
        left--;

        if (left == 0) {
            end = v;
            return true;
        }

        for (int w : G.adj(v)) {
            if (!visited[w]) {
                if (dfs(w, v)) return true;
            }
        }

        visited[v] = false;
        left++;
        return false;
    }

    /**
     * 返回哈密尔顿路径
     *
     * @return
     */
    public List<Integer> result() {
        List<Integer> res = new ArrayList<>();
        if (end == -1) return res;

        int cur = end;
        while (cur != s) {
            res.add(cur);
            cur = pre[cur];
        }
        res.add(s);
        Collections.reverse(res);
        return res;
    }
}

依旧是对这两个图进行测试,首先,我们传入构造器的顶点设置为 0。我的 HamiltonPath 算法类输出的结果如下:

图一

[0, 1, 2, 3]

图二

[0, 1, 2, 3]

从顶点 0 出发,左右两个图均存在哈密尔顿路径。

然后,我们将传入的顶点设置为 2。我的 HamiltonPath 算法类输出的结果如下:

图一

[2, 1, 0, 3]

图二

[]

左图从顶点 2 出发存在哈密尔顿路径;右图如果从顶点 2 出发,则不存在哈密尔顿路径,我们的算法结果输出为空,这与我们的预期结果是保持一致的。

四:状态压缩

在我们的代码中,一直都使用布尔型的 visited 数组来记录图中的每一个顶点是否有被遍历过。在算法面试中,对于像哈密尔顿回路/路径这样的 NP 难问题,通常都会有输入限制,一般情况下,求解问题中给定的图不会超过 30 个顶点。

这样,在算法笔试/面试中,我们就可以对 visited 数组进行状态压缩来优化算法类执行的效率。我们知道一个 int 型的数字有 32 位,每一位不是 1 就是 0,这正好对应了布尔型的 true 和 false。

所以,我们可以将 visited 数组简化成一个数字,该数字的每一个比特位用来表示 visited 数组的每一个 true 或 false 值。

来看一下我们的 HamiltonLoop 中 dfs 的代码:

private boolean dfs(int v,int parent,int left) {
    visited[v] = true; // 待优化...
    pre[v] = parent;
    left--;
    
    if(left == 0 && G.hasEdge(0,v)) {
        end = v;
        return true;
    }
    
    for(int w : G.adj(v))
        if(!visited[w]) // 待优化...
            if(dfs(w,v,left)) return true;
    
    visited[v] = false;
    return false; // 待优化...
}

我们的 dfs 中,涉及到 visited 数组的操作共三处,这三处我已经使用注释标注出来了。

现在,我们的目标就是使用一个数字来代替 visited 数组,如果 visited 是一个 int 型整数,那么这三处操作应该如何用位运算来表示呢?

  1. visited[v] = true;

    如果我们使用整型数字来表示 visited,那么这处的操作就是将数字的第 v 个位置从 0 设置为 1,其位运算操作表示为:

    visited + (1 << i)
    
  2. if(!visited[w])

    如果我们使用整型数字来表示 visited,那么这处的操作就是看数字的第 v 个位置是否为 0,其位运算操作表示为:

    (visited & (1 << i)) == 0
    
  3. visited[v] = false;

    如果我们使用整型数字来表示 visited,那么这处的操作就是将数字的第 v 个位置从 1 设置为 0,其位运算操作表示为:

    visited - (1 << i)
    

所以,我们的 HamiltonLoop 算法类中的 dfs 代码改进为:

private boolean dfs(int v,int parent,int left) {
    visited += (1 << i); // 优化
    pre[v] = parent;
    left--;
    
    if(left == 0 && G.hasEdge(0,v)) {
        end = v;
        return true;
    }
    
    for(int w : G.adj(v))
        if((visited & (1 << i)) == 0) // 优化
            if(dfs(w,v,left)) return true;
    
    visited -= (1 << i)
    return false; // 待优化...
}

优化后的 HamiltonLoop 类和 HamiltonPath 类的具体代码请参考文章最后给出的链接。

五:LeetCode 980.不同路径III

LeetCode 980 号问题是一个 Hard 级别的图论问题。

题目链接:https://leetcode-cn.com/problems/unique-paths-iii/

题目信息:

在二维网格 grid 上,有四种类型的方格:

  • 1 表示起始方格。且只有一个起始方格。
  • 2 表示结束方格,且只有一个结束方格。
  • 0 表示我们可以走过的空方格。
  • -1 表示我们无法跨越的障碍。

返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。

题目分析:

通过题目给定的条件,我们知道,本题实际上就是一道标准的求解哈密尔顿路径的问题。

和普通的求解哈密尔顿路径不同,题目中给定了四种类型的方格,值为 -1 的格子不能走,值为 0 的格子是可以走的,并且 1 和 2 其实也属于 0 这种类型的方格,虽然他们代表的含义是起始点和终点,但是本质上来讲和 0 没什么区别,都是可以走的。

所以,我们在进行 dfs 回溯前,要先进行预处理

第一个就是要更新我们的 left 变量。left 表示的含义是还有多少个顶点没有遍历,初始值为格子的数量。我们需要遍历网格的每一个点,当找到一个值为 -1 的网格就执行一次 left-- 操作。

第二个就是要将值为 1 和 2 的两个格子的值变为 0。并且,我们需要使用两个变量来记录原本值为 1 和 2 所在的格子的信息。具体做法为:遍历 grid 二维网格,记录值为 1 和 2 的格子,赋给变量 start 和 end,并在 grid 中将 1 和 2 的格子赋值为 0。

Java 代码如下:

class Solution {
    private int[][] grid;
    private int r, c;
    private int left;
    private int start;
    private int end;
    private int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};

    public int uniquePathsIII(int[][] grid) {
        this.grid = grid;
        this.r = grid.length;
        this.c = grid[0].length;
        this.left = r * c;
        int visited = 0;

        for (int i = 0; i < r; i++)
            for (int j = 0; j < c; j++)
                if (grid[i][j] == 1) {
                    start = i * c + j;
                    grid[i][j] = 0;
                } else if (grid[i][j] == 2) {
                    end = i * c + j;
                    grid[i][j] = 0;
                } else if (grid[i][j] == -1) {
                    left--;
                }

        return dfs(visited, start);
    }

    int dfs(int visited, int s) {


        visited += (1 << s);
        left--;

        if (s == end && left == 0) {
            // 回溯
            visited -= (1 << s);
            left++;
            return 1;
        }

        int res = 0;
        int x = s / c;
        int y = s % c;
        for (int d = 0; d < 4; d++) {
            int nextX = x + dirs[d][0];
            int nextY = y + dirs[d][1];
            int next = nextX * c + nextY;
            if (isValid(nextX, nextY) && (visited & (1 << next)) == 0 && grid[nextX][nextY] != -1) {
                res += dfs(visited, next);
            }
        }

        // 回溯
        visited -= (1 << s);
        left++;

        return res;
    }

    private boolean isValid(int x, int y) {
        return x >= 0 && x < r && y >= 0 && y < c;
    }
}

六:总结

本篇文章,我介绍了哈密尔顿回路/路径的概念,并且通过一道 LeetCode 题目,进一步加深了我们对求解哈密尔顿回路/路径的算法和回溯思想的理解。

最后,附上文章中的代码所在仓库地址:

https://github.com/jinrunheng/datastructure-and-algorithm

好啦,至此为止,哈密尔顿回路/路径我就介绍完毕了~欢迎大家关注我的公众号【kim_talk】,在这里希望你可以收获更多的知识,我们下一期再见!

憨憨二师兄
关注 关注
  • 32
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
汉密尔顿回路
touso的博客
05-10 3594
一、基本概念 汉密尔顿通路:给定图G,若存在一条经过图中的每一个顶点一次且仅一次的通路,称为汉密尔顿通路。 汉密尔顿回路:若存在一条回路,经过图中的每个顶点一次且进一次,称这条回路为汉密尔顿回路。 汉密尔顿图:具有汉密尔顿回路的图称为汉密尔顿图。 二、相关定理 1.若无向连通图G(V,E)具有汉密尔顿回路,则对于顶点集合V的任意非空子集S,均有W(G-S)<=|S|成立。其中|S|表示集合S中边的数目,W(G-S)表示G-S中连通分量的数目。 2.设G是具有n个顶点的简单图,如果G中每一对顶点度数之和
16.Hamilton(哈密顿)回路问题
假先生智铭
10-03 9467
这个回路问题还好不是很难,就是代码有点多,有很多看不懂 其实哈密顿回路就是说,如上图a中所示,有5个位置点,其中的连线表示两位置点之间可以往来,现在要求从其中某一个点出发,然后遍历所有点后(每个位置点只能走一次)回到起始位置点。它的行进方式还是有很多种的,也没有什么具体条件的要求。 然后b图就是利用回溯算法(其实就是穷尽所有的计算,只不过它的步骤比较系统)来计算可行的行进方式: 假设一开...
哈密顿回路 回溯法——C++代码
05-24
课程的随堂作业,C语言的,用dev就能运行,萌新代码,勿喷,仅仅帮助不想写作业的朋友方便一下,反正老师也不会仔细检查的
无向图汉密尔顿回路c语言程序
12-11
可根据自己的需要修改节点数目,控制台输出,比起递归调用版本,此版本更容易理解。
数据结构哈密顿回路基础
最新发布
m0_63997099的博客
04-19 1105
在一个哈密顿回路中,除了起始和结束的顶点必须是同一个顶点,并且这个顶点恰好出现两次之外,其他每个顶点都恰好出现一次。:如遗传算法、蒙特卡洛方法等,并不保证总是能找到解决方案,但在一些情况下它们可以在多项式时间内给出近似解。:用于求解旅行商问题(TSP),该问题与哈密顿环问题紧密相关。只需要满足条件:每个点经过一次,并且是一个环路就行。:在搜索过程中,如果路径不满足条件,则回退一步。(Hamiltonian Cycle)是图论中的一个概念,指的是在一个图中。,因为需要检查每个顶点的所有排列。
算法笔记】哈密顿问题
繁凡さん的博客
10-09 5431
哈密顿问题 基本概念 哈密尔顿通路:经过图中每个结点且仅经过一次的通路。 哈密尔顿回路:经过图中每个结点且仅经过一次的回路哈密尔顿图:存在哈密尔顿回路的图。 竞赛图:每对顶点之间都有一条边相连的有向图,n 个顶点的竞赛图称为 n 阶竞赛图。 与欧拉回路的对比:欧拉回路是指不重复地走过所有路径回路哈密尔顿回路是指不重复地走过所有点并且最后回到起点的回路。 1.哈密尔顿通路的判定 设一无向图有 n 个顶点,u、v 为图中任意不相邻的两点,deg(x) 代表 x 的度数 若 成立,则图中存在哈密尔顿
哈密顿回路问题
weixin_52734253的博客
06-16 990
问题描述:哈密顿图是一个无向图,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。从一张普通图的任意一点出发,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。问题要求:随机生成无向图,且图中部分顶点间无通路,设计算法找到一条哈密顿回路,实现可视化展示...
图论(三):哈密顿图与哈密顿回路
Machine Learning with Tutors
02-18 1万+
(2)设G是n(n>=3)阶无向简单图,若对于G中的每一对不相邻的顶点u,v,均有d(u)+d(v)>=n-1则G中存在哈密顿通路。(3)在n(n>=2)阶有向图D=中,如果略去所有有向边的方向,所得无向图中含生成子图Kn,则D中存在哈密顿通路。(2)若一个无向图G满足上述(2)中的条件,一个有向图D满足上述(3)的推论的条件,则G、D都是哈密顿图。由推论知,对于完全图Kn,当n>=3时,是哈密顿图,完全二部图Kr,s当r==s>=2时是哈密顿图。设G=为一图(无向图或有向图).G中。
哈密顿图 哈密顿回路 哈密顿通路(Hamilton)
热门推荐
肘子的博客
07-30 5万+
概念:   哈密顿图:图G的一个回路,若它通过图的每一个节点一次,且仅一次,就是哈密顿回路.存在哈密顿回路的图就是哈密顿图.哈密顿图就是从一点出发,经过所有的必须且只能一次,最终回到起点的路径.图中有的边可以不经过,但是不会有边被经过两次.   与欧拉图的区别:欧拉图讨论的实际上是图上关于边的可行便利问题,而哈密顿图的要求与点有关. 判定: 一:Dirac定理(充分条件)   设一个无向...
在有向图中寻找哈密顿回路的快速回溯法
07-13
哈密顿回路的其中一种找法,可供大家参考. 另外对算法有兴趣的同学也可以看看
哈密尔顿回路-图论.rar
06-27
图论算法及其MATLAB 程序代码、哈密尔顿回路、图论及其应用。
Matlab经典算法哈密尔顿回路-其它文档类资源
08-02
提供一种求解最优哈密尔顿算法---三边交换调整法,要求在运行jiaohuan3(三交换法)之前,给定邻接矩阵C和节点个数N,结果路径存放于R中。<br> <br> bianquan.m文件给出了一个参数实例,可在命令窗口...
Matlab经典算法哈密尔顿回路
05-30
提供一种求解最优哈密尔顿算法---三边交换调整法,要求在运行jiaohuan3(三交换法)之前,给定邻接矩阵C和节点个数N,结果路径存放于R中。 bianquan.m文件给出了一个参数实例,可在命令窗口中输入...
有向最短哈密尔顿路问题的DNA算法
01-01
有向最短哈密尔顿路问题的DNA算法是一份整理发布的食品资料文档,只为你能够轻松获取有向最短...该文档为有向最短哈密尔顿路问题的DNA算法,是一份很不错的参考资料,具有较高参考价值,感兴趣的可以下载看看
MATLAB1.rar_matlab次短路_哈密顿回路_回路程序_最小顶点覆盖_覆盖路径
09-19
MATLAB经典算法程序 经典程序。顶点覆盖近似算法哈密尔顿回路,画等温线,模拟退火应用,生成全排列矩阵,随机数的产生,最大流和最小截,最短路和次短路,最短路径,最小生成树Prim算法
哈密顿路径
常胜将军的博客
03-18 1489
题意:给出邻接矩阵,最短Hamilton路径。(给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。) 分析:二进制状压dp,把i看做一个作一个n位二进制数,表示当前所有点的取或不取,dp【i】【j】代表子图i中当前点为j时的最短Hamilton...
哈密顿回路、链路、其他点覆盖问题
nameofcsdn的博客
10-25 1812
。。
哈密尔顿回路matlab
05-27
哈密尔顿回路是指从图的一个顶点出发,恰好经过每个顶点一次,并最终回到出发点的回路。在 MATLAB 中,可以使用 graph 和 hamiltonian 函数来找到一个无向图的哈密尔顿回路。 以下是一个示例代码: ```matlab % 创建一个无向图 G = graph([1 2 2 3 3 4 4 1],[2 3 4 1 4 1 3 2]); % 找到哈密尔顿回路 path = hamiltonian(G); % 显示路径 if ~isempty(path) disp('该图存在哈密尔顿回路:'); disp(path); else disp('该图不存在哈密尔顿回路。'); end ``` 在上面的代码中,我们使用 graph 函数创建了一个无向图,然后使用 hamiltonian 函数找到了该图的哈密尔顿回路。如果找到了哈密尔顿回路,就会输出路径;否则,会输出不存在哈密尔顿回路的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 什么是哈密尔顿回路/路径? 30266
  • 最短路径算法 6637
  • 哈夫曼编码与文件压缩 4028
  • Kruskal 算法与 Prim 算法 2358
  • Java 面试八股文之基础篇(一) 973

最新评论

  • 什么是哈密尔顿回路/路径?

    2301_77086949: ```python def is_valid(v, graph, path, pos): if graph[path[pos-1]][v] == 0: return False if v in path[:pos]: return False return True def hamiltonian_paths_count(graph, path, pos): if pos == len(graph): if graph[path[pos-1]][path[0]] == 1: return 1 else: return 0 count = 0 for v in range(len(graph)): if is_valid(v, graph, path, pos): path[pos] = v count += hamiltonian_paths_count(graph, path, pos + 1) path[pos] = -1 return count nv = int(input()) ne = int(input()) graph = [[0 for _ in range(nv)] for _ in range(nv)] for _ in range(ne): edge = input().split() v1, v2 = int(edge[0]), int(edge[1]) graph[v1][v2] = 1 graph[v2][v1] = 1 path = [-1] * nv path[0] = 0 count = hamiltonian_paths_count(graph, path, 1) print(count)

  • 什么是哈密尔顿回路/路径?

    2301_77086949: def find_hamiltonian_circuits(nv, edges): global hamiltonian_circuits def backtrack(node, visited): if len(visited) == nv: if visited[-1] in [edge[0] for edge in edges if edge[1] == node]: hamiltonian_circuits += 1 return for neighbor in [edge[1] for edge in edges if edge[0] == node and neighbor not in visited]: visited.append(neighbor) backtrack(neighbor, visited) visited.pop() hamiltonian_circuits = 0 for node in range(nv): backtrack(node, [node]) return hamiltonian_circuits nv = int(input()) ne = int(input()) edges = [tuple(map(int, input().split())) for _ in range(ne)] print(find_hamiltonian_circuits(nv, edges))

  • 什么是哈密尔顿回路/路径?

    2301_77086949: def find_hamiltonian_circuits(nv, edges): visited = [False] * nv path = [] hamiltonian_circuits = 0 def backtrack(node): nonlocal hamiltonian_circuits if len(path) == nv and path[0] == path[-1]: hamiltonian_circuits += 1 for neighbor in range(nv): if not visited[neighbor] and (node, neighbor) in edges or (neighbor, node) in edges: visited[neighbor] = True path.append(neighbor) backtrack(neighbor) visited[neighbor] = False path.pop() for v in range(nv): visited[v] = True path.append(v) backtrack(v) visited[v] = False path.pop() return hamiltonian_circuits nv = int(input()) ne = int(input()) edges = set() for _ in range(ne): i1, i2 = map(int, input().split()) edges.add((i1, i2)) print(find_hamiltonian_circuits(nv, edges))

  • 最短路径算法

    Ak5ma1: 原文应该指的是双向的兑换

  • 什么是哈密尔顿回路/路径?

    爱打辅助的小可爱: 博主能不能发一下Graph类的代码

大家在看

  • 【教学类-64-03】20240611色块眼力挑战(三)-2-10宫格&色差10-50(10倍)适合中班幼儿园(星火讯飞) 446
  • 资源共享交流平台毕业设计---SpringBoot+Vue前后端分离
  • 通用大模型VS垂直大模型区别 210
  • 【教学类-12-11】20240612通义万相-动物图片连连看(A4一页3套) 623
  • 什么是进销存?一文读懂进销存管理系统 567

最新文章

  • 浅谈红黑树
  • 最短路径算法
  • Java 面试八股文之基础篇(一)
2021年9篇

目录

目录

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

PHP网站源码横岗网站优化按天收费公司广安网站优化软件推荐忻州百度网站优化报价霍邱设计网站报价沈阳网站改版公司南平关键词按天收费推荐遵义企业网站设计哪家好台州优化多少钱潍坊建站公司贵港企业网站制作多少钱三明建站哪家好甘南网站推广公司泉州网站推广方案公司固原seo报价昌吉网站排名优化报价宜昌百度标王多少钱光明SEO按效果付费公司锦州关键词排名沈阳企业网站设计多少钱海南百姓网标王多少钱平凉关键词排名推荐黔南企业网站设计推荐清远外贸网站建设价格抚顺百度网站优化排名价格南阳模板推广哪家好宝安企业网站设计价格三亚网站优化按天扣费哪家好双龙高端网站设计推荐驻马店企业网站建设报价宁德网站优化推广推荐歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

PHP网站源码 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化