最小生成树
无向图中把2图中的所有边权节点连接起来。要求边长之和最小叫做最小生成树
一个图有不同的生成树 不同的生成树有不同的权值和
类似dijkstra
prim算法
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6+10;
int h[N],e[N],ne[N],nv[N],idx = 0;
void add(int a,int b,int c){
e[idx] = b;
nv[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
int pri[N],dist[N];
bool st[N];
int n,m;
int prim(){
memset(dist,0x3f,sizeof dist);
int res = 0;
dist[1] = 0; // 从1开始
for(int i = 1;i <= n;i ++){ //n个点
int t = -1;
for(int j = 1;j<=n;j++){
if(!st[j]&&(t == -1 || dist[t] > dist[j]))t = j;
}
if(dist[t] == 0x3f3f3f3f){
return -1; // 没有找到
}
res += dist[t]; // 加上
st[t] = 1;
for(int j = h[t];~j;j = ne[j]){
int d = e[j];
int v = nv[j];
if(dist[d]>v&&!st[d]){
dist[d] = v;
pri[d] = t;
}
}
}
// 找到值
return res;
}
int main(){
memset(h,-1,sizeof h);
cin >> n >> m ;
for(int i = 1;i <= m;i ++){
int a,b,c;
cin >> a >> b >> c;
add(a,b,c);
add(b,a,c);
}
int res = prim();
if(res == -1){
cout << "orz" << endl;
}else{
cout << res << endl;
}
return 0;
}
k r u s k a l 算法
结构体存所有的边,两端链接顶点
对所有边进行排序
选出最短不构成环的边,加入最小生成树集合,知道所有点加入
形成环不采取选择
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5;
struct Edge{
int a,b,w;
bool operator <(const Edge &W){ //重写小于号
return w<W.w;
}
}edge[N];
int m,n;
int p[N]; // 并查集
int find(int x){
if(p[x] == x)return p[x];
return find(p[x]);
}
int kruskal(){
sort(edge,edge+m);
for(int i = 1;i<=n;i++)p[i] = i; //初始化并查集
int res = 0,cnt = 0; //// res用于记录最小生成树的权值和,cnt用于记录加入的边数
for(int i=0;i < m;i++){
int a = edge[i].a;
int b = edge[i].b;
int w = edge[i].w;
a = find(a),b = find(b); // 找到a和b的根节点
if(a!=b){ // 如果a和b不在同一个连通分量中
p[a] = b; // 合并连通分量,将a的根节点指向b的根节点
res += w;
cnt ++; //边数++
}
}
if(cnt < n-1)return -1;
return res;
}
int main(){
cin >> n >> m ;
for(int i = 0;i < m;i ++){
int a,b,c;
cin >> a>>b>>c;
edge[i].a = a;
edge[i].b = b;
edge[i].w = c;
}
int res = kruskal();
if(res == -1){
cout << "orz" << endl;
}else{
cout << res << endl;
}
return 0;
}
二分图
两个顶点集 每条边两个顶点分别位于顶点集中,每个顶点集都没有与边直接相连
染色法
判断一个图是不是二分图
对任意没染色的点染色
判断相邻点中没染色,将相邻点染上不同的颜色
如果已经染色,且相邻顶点相同则说明不是二分图
const int N = 1000010;
// 邻接表
int color[N];
bool dfs(int u,int c){
color[u] = c; // 染色该点
for(int i = h[u];~i;i = ne[i]){
int j = e[i];
if(!color[j]){
if(!dfs(j,3-c)) return false;
}else if(color[j]&&color[j]!=3-c){
//处理冲突
return false;
}
}
return true;
}
int main(){
for(int i=1;i<=n;i++){ //遍历点
if(!color[i]){
if(!dfs(i,1)){
cout << "NO" <<endl;
return 0;
}
}
}
cout <<"Yes" << endl;
return 0;
}
匈牙利算法
#include <iostream>
#include <cstring> // 包含 memset 函数
using namespace std;
const int N = 1e5 + 10;
int h[N], e[N], ne[N], idx;
int st[N], match[N]; // st 数组用于标记节点是否已经被访问过,match 数组用于记录匹配信息
int n; // 图中节点的数量
// 添加一条边
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 匈牙利算法的核心函数,寻找增广路径
bool find(int x) {
for (int i = h[x]; ~i; i = ne[i]) {
int b = e[i];
if (!st[b]) { // 如果节点未被访问过
st[b] = 1; // 标记节点为已访问
if (match[b] == 0 || find(match[b])) { // 如果节点未匹配或者与当前节点匹配的节点可以找到增广路径
match[b] = x; // 更新匹配信息
return true; // 找到增广路径
}
}
}
return false; // 未找到增广路径
}
int main() {
int res = 0; // 存储最大匹配数量
for (int i = 1; i <= n; i++) {
memset(st, 0, sizeof st); // 清空标记数组
if (find(i)) res++; // 寻找增广路径,并更新最大匹配数量
}
cout << res << endl; // 输出最大匹配数量
return 0;
}