克鲁斯卡尔:将所有的边按权值排序,每次取最小的一条边,用并查集判断边的两个端点是不是在同一个集合内,如果是,result+=权值,如果不是,继续搜索下一条最短的边,最后判断是否所有的点都在同一个集合内,时间复杂度:e*loge,主要是对边的排序。
prim算法:先从源点开始,将与源点相连的所有边的权值存入min_dis[MAX_N]中,顺便将源点加入used集合,从min_dis[MAX_N]数组中选出一个点,这个点首先是所有点中不在used集合中的,并且在min_dis[MAX_N]中权值最小的,也就是说每个点只会被用到一次,然后再将这个点作为源点,更新max_dis[MAX_N],再重复从max_dis[MAX_N]中选点更新,选出来的点依旧要满足上面两个条件。
迪杰斯特拉:跟prim实在是太像了。。。。别的都一样,只是在更新max_dis[MAX_N]数组时,更新的条件不是map[i][j] < max_dis[j],而是map[s][i] + map[i][j] < max_dis[j]。
匈牙利匹配:说起这个匈牙利匹配就不爽,最讨厌书上或者一些论文中,明明很简单的东西,说的那么高深,最典型的就是高等数学那本书,简直是坑爹,这个匈牙利匹配也是,什么增广路啦,什么找到一条路,两端都不在匹配中,然后取反啦,真心看不懂。按我们土话说,就是:对于两个点的集合A,B,两层循环:
for(i = 遍历A中所有的点)
for(j = 遍历与i有链接的B中的点)
{
if(j没有被匹配)
i 匹配上j
else
跟j已经匹配上的那个点商量下,你能不能跟别的点匹配?如果能跟别的点匹配,那i就跟j匹配,如果不能,继续循环下一个j
}
}
还是拿个标准代码来说吧。。。
bool find(int a) {
for (int i = 1; i <= n2; i++) {//对于一个确定的A的一个点a,遍历与他有关系的B中的点
if (data[a][i] == 1 && !state[i]) { //如果节点i与a有关系并且未被查找过
state[i] = true; //标记i为已查找过
if (result[i] == 0 //如果i还没有对象
|| find(result[i])) { //或者跟i的对象商量下,你能不能跟别的点匹配?
result[i] = a; //能的话,a就跟i匹配上。
return true; //返回查找成功
}
}
}
return false; //没人跟a匹配上,或者是我不接受,不跟别的点匹配
}
int main() {
init();
for (int i = 1; i <= n1; i++) { // 遍历A集合中所有的点
memset(state,0,sizeof(state)); //一个搜索标记,防止无限重复递归搜索
if (find(i)) ans++; //进入函数再说
}
printf("%d\n",ans);
return 0;
}
spfa:说来惭愧,曾经自己写最短路的时候想出来过这个算法,很久之后听说了这个算法,一看,这不是我想出来的么。。。。算法很简单,先从源点s出发,将与s相连的所有的点全部加入队列q,就是普通的队列。剩下的操作从q中取元素i,如果map[s][i] + map[i][k] < dis[k](k是所有与i相连的点),dis[k] = map[s][i] + map[i][k] ,如果k不在队列q中,那么就把k加入q中。如果一个队列入队次数超过n次,那么就说明图中有负权边。