1. Kruskal算法
const int maxn=1e6;
int n,m;//点数,边数
int u[maxn],v[maxn],w[maxn];//第i条边的两个端点序号和权值
int r[maxn];//排序后第i小的边的序号
int cmp(const int i,const int j){ return w[i]<w[j]; }//间接比较函数
int find(int x){//并查集的find
return p[x]==x?x:p[x]=find(p[x]);
}
int kruskal(){
int ans=0;
for(int i=0;i<n;i++) p[i]=i;//初始化并查集
for(int i=0;i<m;i++) r[i]=i;//初始化边序号
sort(r,r+m,cmp);//给边排序
for(int i=0;i<m;i++){
int e=r[i];
int x=find[u[e]];
int y=find[v[e]];//找出当前边两个端点所在集合编号
if(x!=y){
ans+=w[e];
p[x]=y;//如果在不同集合,合并
}
}
return ans;
}
2.一些题
2.1 P3366 【模板】最小生成树
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
int n,m;//点数,边数
int u[maxn],v[maxn],w[maxn];//第i条边的两个端点序号和权值
int r[maxn];//排序后第i小的边的序号
int p[maxn];//i的根结点
int ans,num;
int cmp(const int i,const int j){ return w[i]<w[j]; }//间接比较函数
int find(int x){//并查集的find
return p[x]==x?x:p[x]=find(p[x]);
}
void kruskal(){
ans=0;
for(int i=0;i<n;i++) p[i]=i;//初始化并查集
for(int i=0;i<m;i++) r[i]=i;//初始化边序号
sort(r,r+m,cmp);//给边排序
for(int i=0;i<m;i++){
int e=r[i];
int x=find(u[e]);
int y=find(v[e]);//找出当前边两个端点所在集合编号
if(x!=y){
ans+=w[e];
p[x]=y;//如果在不同集合,合并
num--;
}
}
}
void read_input(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>u[i]>>v[i]>>w[i];
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie=(NULL);
read_input();
num=n;
kruskal();
if(num!=1) cout<<"orz\n";
else cout<<ans<<"\n";
return 0;
}
2.2 UVA1395 苗条的生成树 Slim Span
这道题类似最小生成树,只不过是让最大边权减最小边权最小。采用Kruskal求解最小生成树,只不过我们只是对于每一个边都从它开始Kruskal一遍,来算全所有情况。每一遍都更新一下答案。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
const int inf=0x7fffffff;
struct edge{
int a,b,w;
}e[maxn];
int n,m,a[maxn],b[maxn],w[maxn],fa[maxn],ans,maxed;
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool cmp(edge a,edge b){
return a.w<b.w;
}
bool merg(int x,int y){
x=find(x); y=find(y);
if(x==y) return false;
else fa[y]=x;
return true;
}
void init(){
ans=inf;
for(int i=1;i<=n;i++) fa[i]=i;
sort(e+1,e+m+1,cmp);
}
bool kruskal(int ed){
int now=0;
for(int i=1;i<=n;i++) fa[i]=i;
maxed=-1;
for(int i=ed;i<=m;i++){
if(merg(e[i].a,e[i].b)){
maxed=e[i].w;
if(++now==n-1) return true;
}
}
return false;
}
int main(){
while(~scanf("%d%d",&n,&m)&&n){
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].w);
init();
for(int i=1;i<=m;i++){
if(kruskal(i)){
ans=min(ans,maxed-e[i].w);
}
}
printf("%d\n",ans==inf?-1:ans);
}
return 0;
}
2.3 UVA1151 买还是建 Buy or Build(待补)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<set>
#include<cmath>
#include<sstream>
using namespace std;
#define ll long long
const int mx = 1111;
struct edge{
int u, v, w;
}a[mx * mx], nw[mx*3];
int edge_size; // a为原边, nw为筛出来的边。原边个数需要记录,新边当然是n-1条。
int T, n, m;
string line;
int x[mx], y[mx];//题目条件
int st[11][mx], size[11], mon[11];//套餐
int fat[mx];//并查集
inline int find(int x) { return fat[x] == x ? x : fat[x] = find(fat[x]); }
inline void reset() { for(int i = 1; i <= n; i++)fat[i] = i; }
//建图
inline int diss(int i, int j) { return (x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) * (y[i]-y[j]); }
inline void addedge(int k){
for(int i = 1; i < k; i++) {
edge_size++;
a[edge_size].u = i; a[edge_size].v = k; a[edge_size].w = diss(i, k);
}
}
inline int cmp(edge x, edge y){
return x.w < y.w;
}
//返回花费,tot为已经有了多少边,cnt为要处理的边集合的数量,op为处理旧图还是新图,cost为已经花了多少钱。
inline int Kruskal(int tot, int cnt, int op, int cost){
int sum = cost;
if(op){
for(int i = 1; i <= cnt; i++){
int fu = find(a[i].u), fv = find(a[i].v);
if(fu != fv){
fat[fu] = fv;
tot++;
sum += a[i].w;
if(op)nw[tot] = a[i];
if(tot == n-1)break;
}
}
}
else{
for(int i = 1; i <= cnt; i++){
int fu = find(nw[i].u), fv = find(nw[i].v);
if(fu != fv){
fat[fu] = fv;
tot++;
sum += nw[i].w;
if(tot == n-1)break;
}
}
}
return sum;
}
inline int solve(int cost){
int ans = cost;
for(int S = 0; S < (1<<m); S++){
reset();
int tot = 0;
int need = 0;
for(int q = 1; q <= m; q++){
if(S & (1<<(q-1))){
need += mon[q];
for(int i = 1; i < size[q]; i++){
for(int j = i+1; j <= size[q]; j++){
int u = find(st[q][i]), v = find(st[q][j]);
if(u != v){
fat[u] = v;
tot++;
}
}
}
}
}
ans = min(ans, Kruskal(tot, n-1, 0, need));
}
return ans;
}
int main(){
cin >> T;
while(T--){
edge_size = 0;
// memset(size, 0, sizeof(size));
// memset(fat, 0, sizeof(fat));
getline(cin, line);//有没有都行
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++){
scanf("%d %d", &size[i], &mon[i]);
for(int j = 1; j <= size[i]; j++)scanf("%d", &st[i][j]);
}
for(int i = 1; i <= n; i++){
scanf("%d%d", &x[i], &y[i]);
addedge(i);
}
sort(a + 1, a + edge_size + 1, cmp);
reset();
int cost = Kruskal(0, edge_size, 1, 0);
printf("%d\n", solve(cost));
if(T)putchar('\n');
}
return 0;
}