完美匹配-匈牙利算法(Hungarian method Edmonds)讲解

2023-11-03

目录

匈牙利算法(Hungarian method Edmonds)

例题1 有完美匹配

例题2 无完美匹配

代码实现

变量及函数说明

测试数据1

测试结果1

测试数据2

测试结果


匈牙利算法(Hungarian method Edmonds)

以任意一个匹配M作为开始。(可取M=∅。)

①若M已饱和X的每个顶点,停止(M为完美匹配)。否则,取X中M-不饱和顶点u,今:S<-{u},T=∅。

②若N(S)=T,则停止,算法结束(无完美匹配);否则N(S)⊃T,转到下一步。

③取y∈N(S)\T,若y为M-饱和的,设yz∈M,则令S=S∪(z),T=T∪{y},转步骤②;否则,y为M-不饱和的,存在M-可扩路P,令M=M△E(P),转到步骤①。

M-饱和的:边uv∈M,则称点u与v为M-饱和的。

M-不饱和的:与点w相关联的所有边都不属于M,则称点m为M-不饱和的。

N(S):点集S的邻集,图中所有与S中的点相邻接的顶点的集合。

M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。

M-可扩路(增广路)P:属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,起点与终点都是M-不饱和的

E(P): M-可扩路(增广路)P的边的集合。

M△E(P)=(M∪E(P))\(M∩E(P))

由可扩路(增广路)的定义可以推出下述三个结论:

(1)P的路径个数必定为奇数,第一条边和最后一条边都不属于M

(2)将M和P进行异或操作可以得到一个更大的匹配M,比之前的匹配M多1

(3)M为G的最大匹配当且仅当不存在M的增广路径。

例题1 有完美匹配

从下图中给定的M={x1y2,x5y4}开始,用匈牙利算法求完美匹配。

 

初始图

1)M没有饱和X的每个顶点,取X中M-不饱和的顶点x3,令S={x3},T=∅,则N(S)={y1,y2,y3},N(S)⊃T, 取y3∈N(S)\T,y3为M-不饱和的,找到M-可扩路P=x3y3,令M=M△E(P)={ x1y2, x3y3,x5y4}

添加x3y3

2)M没有饱和X的每个顶点,取X中M-不饱和的顶点x2,令S={x2},T=∅,则N(S)={y1},N(S)⊃T, 取y1∈N(S)\T,y1为M-不饱和的,找到M-可扩路P=x2y1,令M=M△E(P)={ x1y2,x2y1,x3y3,x5y4}

添加x2y1

3)M没有饱和X的每个顶点,取X中M-不饱和的顶点x4,令S={x4},T=∅,则N(S)={y2}, N(S)⊃T, 取y2∈N(S)\T,y2为M-饱和的,且y2x1∈M(y2x1=x1y2),令S=S∪{x1}={x1, x4},T=T∪{y2}={y2}。

N(S)={y2,y4}, N(S)⊃T, 取y4∈N(S)\T,y4为M-饱和的,且y4x5∈M(y4x5=x5y4),令S=S∪{x5}={x1, x4,x5},T=T∪{y4}={y2,y4}。

N(S)={y2,y4,y5}, N(S)⊃T, 取y5∈N(S)\T,y5为M-不饱和的,找到M-可扩路P=x4y2x1y4x5y5,令M=M△E(P)={ x1y2,x2y1,x3y3,x5y4}△{x4y2,y2x1,x1y4,y4x5,x5y5}={ x1y4, x2y1,x3y3,x4y2,x5y5},M包含X的每个顶点,停止。

(可扩路的寻找,可以倒推,y5是由于x5引入的,x5是由于y4引入的,y4是由于x1引入的,x1是由于y2引入的,y2是由于刚开始取的x4)

添加x5y5,x1y4,x4y2,去除x1y2,x5y4

注意:在考试中手写时,不必从与x相连的y中遍历,例如1)并没有选择x3y1,因为很明显x2只连接了y1。当然,你接下来看代码实现时,就没有手写这么简单了,手写时你已经对深搜进行了部分剪枝,请耐心看代码。

手算验证:M为空,x4仅与y2相连,M={x4y2};x2仅与y1相连M={x2y1,x4y2};x3与y1、y2、y3相连,y1与y2已是M-饱和的,x3仅能与y3匹配,M={x2y1,x3y3,x4y2 };x1与y2、y4相连,y2已是M-饱和的,x1仅能与y4匹配,M={x1y4, x2y1,x3y3,x4y2};仅剩x5与y5,两者是边的两点,匹配即可,故存在完美匹配M={ x1y4, x2y1,x3y3,x4y2,x5y5}。

例题2 无完美匹配

从下图中给定M=∅开始,用匈牙利算法求完美匹配。

初始图

1)M没有饱和X的每个顶点,取X中M-不饱和的顶点x1,令S={x1},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y2∈N(S)\T,y2为M-不饱和的,找到M-可扩路P=x1y2,令M=M△E(P)={ x1y2}

添加x1y2

2)M没有饱和X的每个顶点,取X中M-不饱和的顶点x2,令S={x2},T=∅,则N(S)={y1,y2,y4,y5},N(S)⊃T, 取y1∈N(S)\T,y1为M-不饱和的,找到M-可扩路P=x2y1,令M=M△E(P)={ x1y2,x2y1}

添加x2y1

3)M没有饱和X的每个顶点,取X中M-不饱和的顶点x3,令S={x3},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y3∈N(S)\T,y3为M-不饱和的,找到M-可扩路P=x3y3,令M=M△E(P)={ x1y2,x2y1,x3y3}

添加x3y3

M没有饱和X的每个顶点,取X中M-不饱和的顶点x4,令S={x4},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y2∈N(S)\T,y2为M-饱和的,且y2x1∈M(y2x1=x1y2),令S=S∪{x1}={x1, x4},T=T∪{y2}={y2}。

N(S)={ y2,y3}, N(S)⊃T, 取y3∈N(S)\T,y3为M-饱和的,且y3x3∈M(y3x3=x3y3),令S=S∪{x3}={x1, x4,x3},T=T∪{y3}={y2,y3}。

N(S)={y2,y3}=T={y2,y3},结束,没有完美匹配。

注意:手写时可以直接取x1,x3,x4即可,可以看出他们都是连接的y2,y3,两个y没有办法匹配给3个x。

代码实现

通过例子可以看出,S并没有必要,只需要N(S)再原来基础上更新即可,为简化,代码没有加入S。

变量及函数说明

int M[]                                   初始为 - 1, 下标为X下标,值为匹配的Y集合中的元素下标 ,做对称差时覆盖即可
bool X[Maxnum], Y[Maxnum] 初始为false, 用于判断X, Y集合中元素是否为M饱和的
vector<int> P                         可扩路P,初始为空,记录X,Y集合中在P上的元素的下标
set<Vartype> NS, T               对应算法N(S)与T
bool visitedx[Maxnum], visitedy[Maxnum]   每次深搜时标记是否已遍历。

void Init(Graph &G)          初始化函数,参数:图G,功能:初始化图G
void Print(Graph G)          打印图函数,参数:图G,功能:以矩阵形式打印图,可去除
void PrintP(Graph G)        打印路径函数,参数:图G,功能:打印路径P
void PrintM(Graph G)       打印匹配集合M函数,参数:图G,功能:打印匹配集合M
void Delta()                       对称差函数,参数:无,功能:M与E(P)做对称差
void DFS(Graph G,bool x,int start) 深度遍历函数(递归形式)参数:图G,X点集,开始结点下标start 作用:深度遍历,找可扩路

/*
Project: Hungarian method Edmonds
Date:    2020/01/02
Author:  Frank Yu
void Init(Graph &G)				   初始化函数,参数:图G,功能:初始化图G
void Print(Graph G)                打印图函数,参数:图G,功能:以矩阵形式打印图,可去除
void PrintP(Graph G)               打印路径函数,参数:图G,功能:打印路径P
void PrintM(Graph G)               打印匹配集合M函数,参数:图G,功能:打印匹配集合M
void Delta()                       对称差函数,参数:无,功能:M与E(P)做对称差
void DFS(Graph G,bool x,int start) 深度遍历函数(递归形式)参数:图G,X点集,开始结点下标start 作用:深度遍历,找可扩路
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<set>
#include<list>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<iterator>
#include<algorithm>
#include<iostream>
#define Vartype string //顶点类型
#define EdgeType int
#define Maxnum 100 //二部图点集最大数量
using namespace std;
//图的数据结构
typedef struct Graph
{
	Vartype X[Maxnum];
	Vartype Y[Maxnum];
	EdgeType Edge[Maxnum][Maxnum];//边表
	int xnum, ynum,edgenum;//顶点数
}Graph;
//M 下标为X下标,值为匹配的Y集合中的元素下标 初始-1
int M[Maxnum];
//M是否饱和X、Y 饱和为True,不饱和为False
bool X[Maxnum],Y[Maxnum];
//可扩路P
vector<int> P;
//邻接点集合与T集合
set<Vartype> NS, T;
//标记是否已遍历过
bool visitedx[Maxnum], visitedy[Maxnum];
//初始化函数
void Init(Graph &G)
{
	memset(G.Edge, 0, sizeof(G.Edge));
	cout << "请输入X、Y顶点集个数:" << endl;
	cin >> G.xnum >> G.ynum;
	Vartype temp;
	cout << "请输入X顶点集顶点名称:" << endl;
	for (int i = 0; i < G.xnum; i++)
	{
		cin >> temp;
		G.X[i] = temp;
	}
	//for (int i = 0; i < G.xnum; i++) cout << G.X[i] << '\t' << endl;
	cout << "请输入Y顶点集顶点名称:" << endl;
	for (int i = 0; i < G.ynum; i++) 
	{
		cin >> temp;
		G.Y[i] = temp;
	}
	//for (int i = 0; i < G.xnum; i++) cout << G.X[i] << '\t' << endl;
	cout << "请输入边数:" << endl;
	cin >> G.edgenum;
	cout << "请输入边,空格分隔(例如: x y):" << endl;
	Vartype x, y;
	for (int i = 0; i < G.edgenum; i++)
	{	
		cin >> x >> y;
		int p1 = -1,p2 = -1;
		for (int j = 0; j < G.xnum; j++)
			if (!x.compare(G.X[j])) { p1 = j; break; }
		for (int k = 0; k < G.ynum; k++)
			if (!y.compare(G.Y[k])) { p2 = k; break;}
		//cout << p1 << " " << p2;
		if (p1 != -1 && p2 != -1)
		{
			G.Edge[p1][p2] = 1;
		}
		else
		{
			cout << "未找到该边,请检查端点是否输入有误!" << endl;
			break;
		}
	}
}
//打印图函数
void Print(Graph G)
{
	cout << '\t';
	for (int i = 0; i < G.ynum; i++) cout << G.Y[i] << '\t';
	cout << endl;
	for (int i = 0; i < G.xnum; i++)
	{
		cout << G.X[i] << '\t';
		for (int j = 0; j < G.ynum; j++)cout << G.Edge[i][j]<<'\t';
		cout << endl;
	}
}
//输出可扩路
void PrintP(Graph G)
{
	cout << "P:";
	for (int i = 0; i < P.size(); i++)
	{
		if (i % 2 == 0)cout << G.X[P[i]];
		else cout << G.Y[P[i]];
	}
	cout << endl;
}
//输出集合M
void PrintM(Graph G)
{
	bool flag = false;
	cout << "M:{";
	for (int i = 0; i < G.xnum; i++)
	{
		if (M[i] != -1 && !flag) { cout << G.X[i] << G.Y[M[i]]; flag = true; }
		else if (M[i]!=-1&&flag)cout  << ","<< G.X[i] << G.Y[M[i]];
	}
	cout <<"}"<<endl;
}
//集合M与E(P)做对称差
void Delta()
{
	vector<int>::iterator it;
	for (it = P.begin(); it != P.end();it++)
	{
		int x = *it;
		it++;
		int y = *it;
		X[x] = true;
		Y[y] = true;
		M[x] = y;
	}
}
//深度遍历函数(递归形式)参数:图G,X点集开始结点下标start 作用:深度遍历
void DFS(Graph G,bool x,int start)
{
	/*
	cout << "DFS(";
	if (x)cout << "x,";
	else cout << "y,";
	cout << start << ")" << endl;*/
	//X顶点集 
	if (x)
	{
		P.push_back(start);
		cout << "当前路:" << endl;
		PrintP(G);
		visitedx[start] = true;
		for (int i = 0; i < G.ynum; i++) if (G.Edge[start][i] == 1)NS.insert(G.Y[i]);
		if (NS.size() == T.size())
		{
			cout << "N(S)==T,没有完美匹配" << endl;
			system("pause");
		}
		for (int i = 0; i < G.ynum; i++)
		{
			//取Y中M - 饱和顶点
			if (G.Edge[start][i] == 1 && !visitedy[i] && Y[i])//是邻接点且未访问 M - 饱和顶点Y[i]
			{
				T.insert(G.Y[i]);
				cout << "取Y中M - 饱和顶点" << G.Y[i] << endl;
				DFS(G,false,i);//递归深度遍历结点集Y
			}
			//Y为M - 不饱和顶点 找到可扩路P 与M做对称差
			if (G.Edge[start][i] == 1 && !visitedy[i] && !Y[i])
			{
				cout << G.Y[i]<< "为M - 不饱和顶点,找到可扩路"  << endl;
				P.push_back(i);
				PrintP(G);
				Delta();
				PrintM(G);
				//返回步骤一
				for (int i = 0; i < G.xnum; i++)
				{
					memset(visitedx, false, sizeof(visitedx));
					memset(visitedy, false, sizeof(visitedy));
					P.clear();
					NS.clear();
					T.clear();
					//取X中M - 不饱和顶点
					if (!X[i])DFS(G, true, i);
				}
				cout << "找到完美匹配";
				PrintM(G);
				cout << endl;
				system("pause");
			}
		}
		P.pop_back();
		cout << "返回上一层前的路径:" << endl;
		PrintP(G);
		return;//返回至上一层
	}
	else//Y顶点集
	{
		//cout << G.Y[start];
		P.push_back(start);
		cout << "当前路:" << endl;
		PrintP(G);
		visitedy[start] = true;
		for (int j = 0; j < G.xnum; j++)
		{
			if (M[j]==start)//找到Y[start]X[j]属于M
			{
				cout << "存在"<<G.Y[start]<<G.X[j]<<"属于M" << endl;
				DFS(G, true, j);//递归深度遍历结点集X
			}
		}
		P.pop_back();
		cout << "返回上一层前的路径:" << endl;
		PrintP(G);
		return ;//返回至上一层
	}
}
//匈牙利算法
int Hungarian(Graph &G)
{
	int i;
	memset(M, -1, sizeof(M));
	cout << "1.输入初始M  2.M从空集开始" << endl;
	cout << "请选择:";
	cin >> i;
	if (1 == i)
	{
		int num;
		cout << "请输入M中边的数量:" << endl;
		cin >> num;
		cout << "请输入边,空格分隔(例如: x y):" << endl;
		Vartype x, y;
		for (int i = 0; i < num; i++)
		{
			cin >> x >> y;
			int p1 = -1, p2 = -1;
			for (int j = 0; j < G.xnum; j++)
				if (!x.compare(G.X[j])) { p1 = j; break; }
			for (int k = 0; k < G.ynum; k++)
				if (!y.compare(G.Y[k])) { p2 = k; break; }
			if (p1 != -1 && p2 != -1)
			{
				M[p1] = p2;
				X[p1] = true;
				Y[p2] = true;
			}
			else
			{
				cout << "未找到该边,请检查端点是否输入有误!" << endl;
				break;
			}
			
		}
	}
	PrintM(G);
	//步骤1 判断M是否饱和所有X元素
	for (int i = 0; i < G.xnum; i++)
	{
		memset(visitedx, false, sizeof(visitedx));
		memset(visitedy, false, sizeof(visitedy));
		P.clear();
		NS.clear();
		T.clear();
		//取X中M - 不饱和顶点
		if (!X[i])DFS(G, true, i);
	}
	cout << "找到完美匹配"; 
	PrintM(G);
	cout<< endl;
	return 0;
}
//主函数
int main()
{
	Graph G;
	Init(G);
	Print(G);
	Hungarian(G);
	return 0;
}

以上面两个例题做测试

测试数据1

5 5
x1 x2 x3 x4 x5
y1 y2 y3 y4 y5
10
x1 y2
x1 y4
x2 y1
x3 y1
x3 y2
x3 y3
x4 y2
x5 y2
x5 y4
x5 y5
1 2
x1 y2
x5 y4

测试结果1

部分结果
部分结果

 

实际深搜树

测试数据2

5 5
x1 x2 x3 x4 x5
y1 y2 y3 y4 y5
12
x1 y2
x1 y3
x2 y1
x2 y2
x2 y4
x2 y5
x3 y2
x3 y3
x4 y2
x4 y3
x5 y3
x5 y5
2

测试结果

部分结果
部分结果

 

更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

完美匹配-匈牙利算法(Hungarian method Edmonds)讲解 的相关文章

  • LeetCode 127. 单词接龙(C++)*

    思路 1 如果采用回溯法来的话会超时 2 这里采用构造图和广度优先遍历结合来实现 首先要构造图 需要将每个字符串对应一个数字id 然后边的构造使用矩阵来实现 这里采用将每一个字符串的id连接每个将该字符串的其中一个字符改为未知字符的字符串的
  • Python 邻接矩阵实现无向图、有向图的三种方法,并绘图显示

    网上查了很多资料 发现主要是使用邻接表来实现图 并进行遍历的 而采用邻接矩阵的就非常少 不得已 就只有闭门造车 埋头苦修 小有成果 供后来学习者研究 通过二维数组建立无向图 通过二维数组建立有向图 通过边建立有向图 为方便查看 通过Netw
  • 弗洛伊德算法(floyd)

    算法背景 图中的A B C D E F G 代表7个村庄 边上的权值带表两村庄之间的距离 现在要从某一个村庄 例如G村庄 往另外几个村庄送邮件 问任意一村庄到其他各村庄的最短距离分别是多少 思路 该问题实际是求从任一顶点到其余顶点的最短距离
  • 1399: 最小生成树

    题目描述 最小生成树问题是实际生产生活中十分重要的一类问题 假设需要在n个城市之间建立通信联络网 则连通n个城市只需要n 1条线路 这时 自然需要考虑这样一个问题 即如何在最节省经费的前提下建立这个通信网 可以用连通网来表示n个城市以及n个
  • 完全理解图(上)——图的概念、存储及遍历

    术语 图 由结点的有穷集合V和边的集合E组成 在图中 结点常被称为顶点 若两个顶点之间存在一条边 则表示两个顶点相邻 有向图 图的每条边都有方向 无向图 图的每条边没有方向 弧 有向图中 常将边称为弧 含箭头的一端称为弧头 另一端称为弧尾
  • uva 1601 The Morning after Halloween

    题目 The Morning after Halloween 题意 有n个用小写字母表示的鬼和一张地图 每个鬼都要移动到对应的大写字母 两个鬼的位置不能在一次移动中交换 问最少步数 思路 bfs 1 先将地图用图的方法表示 即在每一个空白
  • 普利姆算法(Prim)

    普利姆算法和克鲁斯卡尔算法都是求连接图中所有结点的最短路径 也就是最小生成树 普利姆算法其实就是不断获取已经访问结点和未访问结点之间的最短边来获取所有结点间的最短路径 也可以认为是广度 贪婪 接下来看算法的实现 这里只给出关键代码 基本的图
  • 数据结构知识整理

    基于严蔚敏及吴伟民编著的清华大学C语言版教材并结合网上相关资料整理 http www docin com p 2027739005 html 第一章 绪论 1 数据结构 是一门研究非数值计算的程序设计问题中计算机的操作对象以及他们之间的关系
  • 完美匹配-匈牙利算法(Hungarian method Edmonds)讲解

    目录 匈牙利算法 Hungarian method Edmonds 例题1 有完美匹配 例题2 无完美匹配 代码实现 变量及函数说明 测试数据1 测试结果1 测试数据2 测试结果 匈牙利算法 Hungarian method Edmonds
  • 数据结构之图:无向图的介绍与功能实现,Python——22

    无向图 Undigraph 的介绍 引入 生活中的图 有地图 集成电路板的图 可以看类似的看做是数据结构中的图 数据有 一对一 一对多 和 多对多 的关系 前两种分别表示线性表和树的存储结构性质 而多对多则可表示图的存储结构性质 定义 图是
  • 图(3)--拓扑排序与关键路径

    一 拓扑排序 1 定义 拓扑排序可以理解为在有向图无环图AOV 网 Activity On Vertex 用图的顶点表示活动 用弧表示活动之间的优先级 中排成一个具有前后次序的线性序列 2 实现方式 1 输入AOV网络 令 n 为顶点个数
  • 使用阿里云日志服务来分析日志

    随着云服务技术越来越成熟 作为一枚运维 不得不感慨云计算的发展对我的职业生涯起了积极推动的作用 一方面我可以通过云服务来提高我的工作效率 另一方面我节省了更多时间来学习 在提高我专业度的同时 个人能力也越来越强 在此我就以阿里云日志服务 给
  • 弗洛伊德算法(floyd)

    弗洛伊德算法和迪杰斯特拉算法都是求两点之间最短路径的问题 弗洛伊德算法使用了动态规划的思想 用二维矩阵记录了所有点之间最短的距离 虽然代码只有几行 但是思想还很值得回味的 其主要的思想就是两个点之间的直接距离能否使用第三个点来缩短 公式 v
  • 一张图弄明白开源协议-GPL、BSD、MIT、Mozilla、Apache和LGPL 之间的区别

    导读 在开源软件中经常看到各种协议说明 GPL BSD MIT Mozilla Apache和LGPL 这些协议之间的有什么区别 如何选择合适的开源协议 请看下文 特作记录一篇 以供后续查看 参考 阮一峰的网络日志
  • 最小生成树算法之Prim算法

    生成树 一个连通图的生成树是一个极小连通子图 它含有图中全部n个顶点和构成一棵树的 n 1 条边 连通图由一次遍历就可以产生生成树 由深度优先遍历得到的生成树称为深度优先生成树 由广度优先遍历得到的生成树称为广度优先生成树 一个连通图的生成
  • 图的邻接矩阵存储

    public class Graph init public static int MAX GRAPH SIZE 256 最大顶点个数 public static int MAX WEIGHT 65536 图中最大权值 public int
  • 克鲁斯卡尔算法(kruskal)

    我自己感觉 克鲁斯卡尔算法比普利姆算法更好理解 它就两个要点 排序和判断是否成环 排序 我们把两两相邻的边根据边的权值 从小到大依次排序 这个十大排序算法可以自己选一个去实现下 刚好还可以回忆下以前的算法 下面我们使用冒泡来实现边的排序 是
  • 数据结构--图的应用--最短路径

    最短路径 两种常见的最短路径问题 一 单源最短路径 用Dijistra迪杰斯特拉 算法 二 所有顶点间的最短路径 用Floyd 弗洛伊德 算法 Dijistra算法 1 初始化 先找出从源点v0到各终点Vk的的直达路径 V0 Vk 即通过一
  • 图的应用--Prim算法

    图的应用 Prim算法 Prim算法是一种基于顶点的贪心算法 从起始顶点出发 每次迭代选择当前可用的最小权值边 然后把边上依附的其他顶点加入最小生成树 prim算法可以称为 加点法 比较适合稠密图 算法思想 设G V E 是一个加权连通图
  • 图(一)之邻接表Adjacency List

    开始攻克图的算法 先从最简单的存储开始实现 本文关于邻接表的实现 邻接表是图的存储中最简单也是最基本的存储结构 基于链表的思想实现的 在邻接表中 对于中的每个顶点建立一个单链表 第i个单链表中的节点表示依附于顶点的vi的边 每个节点由3个域

随机推荐

  • HOOK startActivity

    往期推荐 HOOK框架 动态代理 HOOK框架 静态代理 实战案列分析 Crakeme01 实战 AliCrackme 需要相关资料的朋友 可以 加入此处即可打包获取 Hook的英文含义是钩子 你可以理解为用钩子把要Hook的对象勾过来 然
  • Error:(31, 39) java: 不兼容的类型: java.lang.String无法转换为int 的处理

    异常说明 使用增强for输出元素的时候 在元素名前面加了数组名称 发生原因 未严格按照增强for的语法写代码 改正方式 按each for的语法 不要在输出元素前面加数组名 改正前 改正后 转载于 https www cnblogs com
  • State:状态模式

    某个对象有一个标准接口 同时 该对象可能处于各种状态下 当该对象处于不同状态下时 客户调用标准接口 该对象会产生不同的响应 即允许一个对象在其内部状态改变时改变它的行为 使对象看起来似乎修改了它的类 这就是状态模式 例如 电灯开关这个对象提
  • Flutter

    在 decoration 里面设置 image 即可 Container width 100 height 100 alignment Alignment center child Text 有背景图 decoration BoxDecor
  • Python玩转微信 个性签名生成词云图

    本博客代码参照http www cnblogs com jiaoyu121 p 6944398 html 运行如下代码之前 将下图的wechat jpg放入F盘下 需安装itchat jieba matplotlib numpy和wordc
  • 03 什么是预训练(Transformer 前奏)

    博客配套视频链接 https space bilibili com 383551518 spm id from 333 1007 0 0 b 站直接看 配套 github 链接 https github com nickchen121 Pr
  • Fsm ps2

    The PS 2 mouse protocol sends messages that are three bytes long However within a continuous byte stream it s not obviou
  • SpringBoot整合MyBatis-Plus各种使用点超级详细

    简介 MyBatis Plus是一个Mybatis增强版工具 在MyBatis上扩充了其它功能没有改变基本功能 也就是只做封装增强不做改变 为了简化开发提交效率而存在 不需要写XML等sql语句就可实现 官方文档 https mp baom
  • Python全栈开发【基础-09】深浅拷贝+while循环

    专栏介绍 本专栏为Python全栈开发系列文章 技术包括Python基础 函数 文件 面向对象 网络编程 并发编程 MySQL数据库 HTML JavaScript CSS JQuery bootstrap WSGI Django Flas
  • xss跨站之waf绕过及安全修复(28)

    手工探针xss绕过waf规则 打开靶场 写入跨站测试语句发现拦截 这里就做一个最经典的方法 做一个关键字拆分来确立拦截的是什么东西 去掉最后字符串 访问还是拦截 再去掉alert 1 访问还是拦截 再去掉尖括号里面一个字符 留下scrtp在
  • Linux学习之旅-《Linux就该这么学》—— 第十六天

    DHCP 作用域 一个完整的IP地址段 DHCP协议根据作用域来管理网络的分布 分配IP地址及其他配置参数 超级作用域 用于管理处于同一个物理网络中的多个逻辑子网段 超级作用域中包含了可以统一管理的作用域列表 排除范围 把作用域中的某些IP
  • Devos勒索病毒的最新威胁:如何恢复您的数据?

    引言 在黑客与安全专家之间的无尽较量中 devos勒索病毒是一种异想天开的数字杀手 它以黑客们独具匠心的方式 病毒攻击着受害者的数据 挟持着宝贵信息 引发一场数据恢复的奇妙探险 本文91数据恢复将为你揭示 devos勒索病毒的不同寻常之处
  • 软测面试了一个00后,绝对能称为是内卷届的天花板

    前言 公司前段缺人 也面了不少测试 结果竟然没有一个合适的 一开始瞄准的就是中级的水准 也没指望来大牛 提供的薪资也不低 面试的人很多 但平均水平很让人失望 令我印象最深的是一个00后测试员 他技术基础方面确实还不错 面试也表现的非常自信
  • 有什么哪些便宜的云服务器推荐?

    竞争激烈的时代 优惠永远不会少 1元团购 1元打车 0元骑单车 1元吃外卖 1元游景区 1元XXX等等 正是由于激烈的竞争 我们才可以享受折扣力度如此大的活动 谁让这是互联网时代呢 得用户者才得天下 云计算时代 亦是如此 在新基建的引导下
  • 解决报错:@org.springframework.beans.factory.annotation.Autowired(required=true)

    先把问题贴出来 org springframework beans factory annotation Autowired required true 报这个错是因为 Autowired required true 当使用 Autowir
  • 利用Spring-MVC访问数据库

    1 创建相关的项目 首先相关路径 在pom xml中添加具有Spring mvc mysql dbcp的三个包 2 首先创建Admin类与数据库信息对应 2 1 数据库 2 2 jdbc properties 名称不可改变 user是数据库
  • Python读写CSV文件--追加方式多空行

    说明 文章首发于HURUWO的博客小站 本平台做同步备份发布 如有浏览或访问异常图片加载失败或者相关疑问可前往原博客下评论浏览 原文链接 Python读写CSV文件 追加方式多空行直接点击即可前往访问 更多技术文章访问本人博客HuRuWo的
  • 3dmax文件打不开了,怎么办

    3dmax文件 有时候原来操作还是好好的 下一次或者隔天再打开的时候 就会出现错误 无论怎么开 都开不了 这个该怎么办 其实这种情况 还真不好办 因为很多情况下 这种文件基本是损坏了 再打开会比较难了 造成这样的原因多种多样 有一种是病毒引
  • 用Vue.js开发一个电影App的前端界面

    我们要构建一个什么样的App 我们大多数人使用在线流媒体服务 如Netflix 观看我们最喜欢的电影或者节目 这篇文章将重点介绍如何通过使用vue js 2 建立一个类似风格的电影流媒体WEB交互界面 见上图 最终的产品可以去这里找 htt
  • 完美匹配-匈牙利算法(Hungarian method Edmonds)讲解

    目录 匈牙利算法 Hungarian method Edmonds 例题1 有完美匹配 例题2 无完美匹配 代码实现 变量及函数说明 测试数据1 测试结果1 测试数据2 测试结果 匈牙利算法 Hungarian method Edmonds