最小生成树与二分图
最小生成树:
prim算法类似于dijkstra算法,也分为朴素版和堆优化版,朴素版适用于稠密图,而堆优化版的适用于稀疏图,时间复杂度也与dijkstra算法相似,而kruskal算法的时间复杂度是mlogm的,且思路比堆优化版的prim算法简单。所以稠密图我们用朴素prim,稀疏图我们使用kruskal算法。
二分图:
判断二分图使用染色法dfs,匈牙利算法求二分图的最大匹配,非常重要。
1、prim算法
最小生成树没有环将所有点距离设置成无穷大,然后迭代n次,找到当前距离已确定点集合距离最小的点,即集合外的最近的点,然后用这个点去更新其他点到集合的距离。点到集合的距离定义为,这个点到集合中点的所有边的长度最小。最小生成树是指当前确定该点的话,就把选中该点时的那条边加入树中。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510, INF=0x3f3f3f3f;
int dist[N], g[N][N];
bool st[N];
int n, m;
int prim()
{
int res=0;
memset(dist, 0x3f, sizeof dist);
for(int i=0;i<=n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!st[j] && (t == -1 || dist[t] > dist[j])) t=j;
}
if(i && dist[t] == INF) return INF;
if(i) res += dist[t];
st[t]=true;
for(int j=1;j<=n;j++)
{
dist[j] = min(dist[j], g[t][j]);
}
}
return res;
}
int main()
{
memset(g, 0x3f, sizeof g);
scanf("%d%d", &n, &m);
while(m--)
{
int a, b, w;
cin>>a>>b>>w;
g[a][b] = g[b][a] = min(g[a][b], w);
}
int t=prim();
if(t == INF) puts("impossible");
else cout<<t<<endl;
return 0;
}
堆优化思路与dijkstra一样,就是用堆来找当前最近即可。过于繁杂且少用,不讲。
2、kruskal算法
kruskal算法可以看成二分加并查集,先根据边的权值排序,然后遍历,如果ab不连通的话,就在ab间这条边加入连通集合里,类似并查集的询问和合并操作。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=200010;
int res, cnt;
int p[N];
int n, m;
struct edge{
int a, b, w;
bool operator < (const edge &X) const
{
return w < X.w;
}
}edges[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++) p[i] = i;
for(int i=0;i<m;i++)
{
int a, b, w;
cin>>a>>b>>w;
edges[i]={a, b, w};
}
sort(edges, edges +m);
for(int i=0;i<m;i++)
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if(a != b)
{
res += w;
cnt++;
p[a] = b;
}
}
if(cnt < n-1) puts("impossible");
else cout<<res<<endl;
return 0;
}
3、染色法
二分图当且仅当图中没有奇数环。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10, M = 2e5+10;
int h[N], e[M], ne[M], idx;
int n, m;
int color[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool dfs(int u, int c)
{
color[u] = c;
for(int i=h[u];i!=-1;i=ne[i])
{
int j = e[i];
if(!color[j])
{
if(!dfs(j, 3-c)) return false;
}
else if(color[j] == c) return false;
}
return true;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
while(m--)
{
int a, b;
cin>>a>>b;
add(a, b), add(b, a);
}
int flag = true;
for(int i=1;i<=n;i++)
{
if(!color[i])
{
if(!dfs(i, 1))
{
flag = false;
break;
}
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
4、匈牙利算法
找最大匹配数
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 510, M = 1e5 + 10;
int h[N], e[M], ne[M], idx;
int match[N];
bool st[N];
int n1, n2, m;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int find(int x)
{
for(int i=h[x];i!=-1;i=ne[i])
{
int j = e[i];
if(!st[j])
{
st[j]=true;
if(match[j] == 0 || find(match[j]))
{
match[j] = x;
return true;
}
}
}
return false;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d%d", &n1, &n2, &m);
while(m--)
{
int a, b;
cin>>a>>b;
add(a, b);
}
int res=0;
for(int i=1;i<=n1;i++)
{
memset(st, false, sizeof st);
if(find(i)) res++;
}
cout<<res<<endl;
return 0;
}