最小生成树与二分图模板记忆
最小生成树与二分图知识架构
最小生成树{
Prim algorithm{
1.朴素版Prim O(O^2) 稠密图{
将所有点初始化为正无穷
迭代n次
找到集合外最近的点
判断是不是这个点是不是连通的
如果不是第一条边就进行累加
用这个点更新集合外的点到集合的距离(不更新累加距离,只更新最短距离)
标记这个集合并加入集合
}
2.堆优化Prim O(mlogn) 稀疏图{//一般不用,用克鲁斯卡尔代替
这里就先不写了...
}
}
Kruskal algorithm{ O(mlogm) 稀疏图
1.将所有边按权重从小到大排序 O(mlogm)
2.初始化并查集
3.从小到大枚举每条边(a-b权重c),若a,b不连通,将a,b加入集合中
}
}
二分图
二分图{//一个图是二分图当且仅当图中不含奇数环
1.染色法{ O(n+m)
1.建邻接表
2.for(1~n)
if(i未染色)
dfs(i)
需要标记每个点是否被染色
}
2.匈牙利算法{ O(nm),实际运行时间远小于O(nm)
最快时间内得出二分图成功匹配的最大的数量(成功匹配:没有两条边共用一个点)
}
最大流算法
}
最小生成树
Prim algorithm
#define MAXN 510
#define INF 0x3f3f3f3f
int n,m;
int g[MAXN][MAXN],dis[MAXN];
bool st[MAXN];
int prim(){
memset(dis,0x3f,sizeof(dis));
int res=0;
//n次迭代
for(int i=1;i<=n;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dis[t]>dis[j]))
t=j;
if(i!=1&&dis[t]==INF) return INF;
if(i!=1) res+=dis[t];
for(int j=1;j<=n;j++)
dis[j]=min(dis[j],g[t][j]);
st[t]=true;
}
return res;
}
### 二分图算法
二分图{//一个图是二分图当且仅当图中不含奇数环
1.染色法{ O(n+m)
1.建邻接表
2.for(1~n)
if(i未染色)
dfs(i)
需要标记每个点是否被染色
}
关键的bool dfs:
1.标记颜色
2.遍历此点所有连接的点,如果没有被染色,则染色并dfs,若返回false 则返回false(这里的迭代十分关键,看代码)
3.否则如果染过颜色,则判断颜色是都矛盾
4.都没问题返回true
代码:
bool dfs(int x,int k){
color[x]=k;
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
if(!color[j]){
if(!dfs(j,3-k)) return false;
}
else if(color[j]==k) return false;
}
return true;
}
//之后的main函数内:
for(int i=1;i<=n;i++){
if(!color[i]){
if(!dfs(i,1)){
flag=false;
break;
}
}
}
if(!flag) puts("No");
else puts("Yes");
### 匈牙利算法
遍历二分图要找的点每一个对应点
如果此点不在配对成功的集合中{
将此点加入配对成功的集合
如果此映射点没有被配对或者将其配对的点有其他可配对的点,则返回配对成功(对应函数外侧更新答案)
}
代码:
bool 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;
}