0
点赞
收藏
分享

微信扫一扫

哈密顿回路、链路、竞赛图


目录

​​一,哈密顿回路、哈密顿链路​​

​​二,Dirac定理​​

​​三,算法​​

​​力扣 996. 正方形数组的数目​​

​​力扣 980. 不同路径 III​​

​​四,竞赛图​​

​​五,相关puzzle​​

​​1,数字满格​​

​​2,马的哈密顿回路、链路​​

一,哈密顿回路、哈密顿链路

图的哈密顿回路是指包含图的所有节点的回路。

图的哈密顿链路是指包含图的所有节点的链路。

二,Dirac定理

如果G是一个n(n≥3)个点的无向简单图,所有节点的度数都 ≥ n/2.0,则G中存在哈密顿回路。

证明过程:

(1)G是连通图

(2)设最长路径是L,用抽屉原理可以证明L是个环

(3)如果有节点不在环中,根据连通性可以加入一个点把环变成一条更长的路径。所以所有节点都在这个环中。

三,求解哈密顿链路

哈密顿问题其实和TSP问题差不多,是NP问题,没有特别好的算法。

回溯法:

class Hamilton
{
public:
stack<int> hami;//哈密顿链路
Hamilton(int n, map<int, vector<int>>& m, int type)//type=0是无向图 1是有向图
{
this->n = n;
this->m = m;
this->type = type;
for (int i = 0; i < n; i++)dfs(i);
}
private:
bool dfs(int k)
{
s.push(k);
if (s.size() == n) {
hami = s;
return true;
}
for (auto nk : m[k]) {
if (visit[k])continue;
visit[k] = 1;
if (dfs(nk))return true;
visit[k] = 0;
}
s.pop();
return false;
}
int n;
int type;
map<int, vector<int>> m;//邻接表
map<int, int>visit;
stack<int>s;
};

力扣 996. 正方形数组的数目

给定一个非负整数数组 ​​A​​,如果该数组每对相邻元素之和是一个完全平方数,则称这一数组为正方形数组。

返回 A 的正方形排列的数目。两个排列 ​​A1​​​ 和 ​​A2​​​ 不同的充要条件是存在某个索引 ​​i​​,使得 A1[i] != A2[i]。

示例 1:


输入:[1,17,8] 输出:2 解释: [1,8,17] 和 [17,8,1] 都是有效的排列。


示例 2:


输入:[2,2,2] 输出:1


提示:

  1. ​1 <= A.length <= 12​
  2. ​0 <= A[i] <= 1e9​

vector<int> gnums;
class Hamilton
{
public:
map<long long, int>ans;
Hamilton(int n, map<int, vector<int>>& m, int type)//type=0是无向图 1是有向图
{
this->n = n;
this->m = m;
this->type = type;
for (int i = 0; i < n; i++)dfs(i,1);
}
private:
bool dfs(int k,int deep)
{
if (visit[k])return false;
long long h2 = h;
hash(k);
if (hashVisit[h])
goto RET;
hashVisit[h] = 1;
if (deep == n) {
ans[h] = 1;
h = h2;
return true;
}
visit[k] = 1;
for (auto nk : m[k]) {
dfs(nk,deep+1);
}
RET:
visit[k] = 0;
h = h2;
return false;
}
void hash(int k)
{
h = ((h * (gnums[k]+123) + 666) * 789)%1000000007;
}
int n;
int type;
map<int, vector<int>> m;//邻接表
map<int, int>visit;
map<long long, int>hashVisit;
long long h = 1234567;
};

class Solution {
public:
int numSquarefulPerms(vector<int>& nums) {
int n = nums.size();
map<int, vector<int>>m;
for (int i = 0; i < n; i++)for (int j = i + 1; j < n; j++) {
if (isSquare(nums[i] + nums[j]))m[i].push_back(j), m[j].push_back(i);
}
gnums = nums;
return Hamilton(n, m, 0).ans.size();
}
bool isSquare(int n) {
int x = sqrt(n);
return x * x == n;
}
};

力扣 980. 不同路径 III

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

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

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

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

示例 1:


输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)


示例 2:


输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] 输出:4 解释:我们有以下四条路径: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)


示例 3:


输入:[[0,1],[2,0]] 输出:0 解释: 没有一条路能完全穿过每一个空的方格一次。 请注意,起始和结束方格可以位于网格中的任意位置。


提示:

  • ​1 <= grid.length * grid[0].length <= 20​

class Hamilton
{
public:
map<long long, int>ans;
Hamilton(int n, map<int, vector<int>>& m, int type,int start,int e)//type=0是无向图 1是有向图
{
this->n = n;
this->m = m;
this->type = type;
this->e = e;
dfs(start,1);
}
private:
bool dfs(int k,int deep)
{
if (visit[k])return false;
long long h2 = h;
hash(k);
if (hashVisit[h])
goto RET;
hashVisit[h] = 1;
if (deep == n) {
ans[h] = 1;
h = h2;
return true;
}
visit[k] = 1;
for (auto nk : m[k]) {
if (deep < n - 1 && nk == e)continue;
dfs(nk,deep+1);
}
RET:
visit[k] = 0;
h = h2;
return false;
}
void hash(int k)
{
h = ((h * (k+123) + 666) * 789)%1000000007;
}
int n;
int type;
map<int, vector<int>> m;//邻接表
map<int, int>visit;
map<long long, int>hashVisit;
long long h = 1234567;
int e;
};


class Solution {
public:
int uniquePathsIII(vector<vector<int>>& grid) {
row=grid.size(),col = grid[0].size();
int n = row*col;
map<int, vector<int>>m;
int s, e;
for (int i = 0; i < row; i++)for (int j = 0; j < col; j++) {
if (grid[i][j] == -1) {
n--;
continue;
}
if (grid[i][j] == 1)s = id(i, j);
if (grid[i][j] == 2)e = id(i, j);
vector<int> v = getNeighbor4(id(i, j));
for (int k : v)if(grid[k/col][k%col]!=-1)m[k].push_back(id(i, j));
}
return Hamilton(n, m, 0,s,e).ans.size();
}
int id(int x, int y)
{
return x * col + y;
}
vector<int> getNeighbor4(int k)
{
vector<int>ans;
if (k >= col)ans.push_back(k - col);
if (k < (row - 1) * col)ans.push_back(k + col);
if (k % col)ans.push_back(k - 1);
if (k % col < col - 1)ans.push_back(k + 1);
return ans;
}
int row,col;
};

四,竞赛图

竞赛图必有哈密顿链路。

哈密顿回路、链路、竞赛图_算法

 

五,相关puzzle

1,数字满格

​​4399在线play​​

每个格子有8个邻居(部分格子可能只有3-7个邻居),按照这种关系把100个格子转化成无向图,寻找此图的曼哈顿链。

哈密顿回路、链路、竞赛图_算法_02

到90是比较容易的,到100就比较难了。

哈密顿回路、链路、竞赛图_哈密顿回路_03

2,马的哈密顿回路、链路

定理:

如果n是偶数(n>4)那么n*n的棋盘有一个哈密顿回路。

如果n是奇数(n>3)那么n*n的棋盘有一个哈密顿链路。

哈密顿回路、链路、竞赛图_哈密顿回路_04

像这样的图还可以构造一些,不过基本上都差不多。

总结起来就是,最中间的格子是比较特殊的,上图中标号1-8的这8个格子构成1个回路,

其余的16个格子也构成1个回路,相当于5*5的棋盘其实就是3个回路组合而成,只要适当地选择断点,即可把3个回路连接成一条链。

如果选取的起点不同,得到的哈密顿链还是略有区别的。

比如:​​112象棋(12)​​

关于上面的2个定理,书上的意思是用数学归纳法来证明,从n=k变成n=k+4相当于在原来的基础上,套上了一个宽度为2的边框。这个边框是若干个哈密顿回路组成的,只要在适当的地方断开,就可以和里面的k*k连接起来。

下面讨论,7*7的棋盘去掉中间的3*3如何形成哈密顿回路。

哈密顿回路、链路、竞赛图_链路_05

首先,角落的点只有2个邻居,那么在回路里面,这2个点也一定都是他的邻居。

其他的,以此类推,可以得到下图:

哈密顿回路、链路、竞赛图_哈密顿回路_06

对于那些已经连了2条线的点,自然是没有任何悬念了,所以接下来只需要考虑那些恰好连了1条线的点该如何连接起来。将上图化简如下:

哈密顿回路、链路、竞赛图_哈密顿回路_07

黑色的线表示一定相连,红色的线表示可能相连。

这样,可以得到这16个点的哈密顿回路:

哈密顿回路、链路、竞赛图_哈密顿回路_08

与此对应,可以得到原图:

哈密顿回路、链路、竞赛图_数组_09

这是由2个哈密顿回路构成的。

这样,整个通过把大的回路断开一个地方,可以得到下图:

哈密顿回路、链路、竞赛图_链路_10

在把紫色的哈密顿回路和这个长为41的哈密顿链连接起来,即可得到7*7的哈密顿链

举报

相关推荐

0 条评论