最小生成树

P3366 【模板】最小生成树

prim 算法邻接矩阵存图

//prim
#include <bits/stdc++.h>
using namespace std;
int n, m, g[5010][5010], minn[5010], asd, u, v, w;
bool vis[5010];
int main()
{
    scanf("%d %d", &n, &m);
    memset(g, 0x3f, sizeof(g));
    memset(minn, 0x3f, sizeof(minn));
    while(m--){
        scanf("%d %d %d", &u, &v, &w);
        g[u][v]=min(g[u][v], w);
        g[v][u]=min(g[v][u], w);
    }
    minn[1]=0;
    for(int i=1; i<=n; ++i){
        int minid=0;
        int minedge=0x3f3f3f3f;
        for(int j=1; j<=n; ++j){
            if(!vis[j] && minn[j]<minedge){
                minid=j;
                minedge=minn[j];
            }
        }
        vis[minid]=true;
        for(int j=1; j<=n; ++j){
            //必须确保j是没加到生成树的点,才可更新 minn 值
            if(!vis[j]){
                minn[j]=min(minn[j], g[minid][j]);
            }
        }
    }
    for(int i=1; i<=n; ++i){
        if(minn[i]==0x3f3f3f3f){
            puts("orz");
            return 0;
        }
    }
    for(int i=1; i<=n; ++i){
        asd+=minn[i];
    }
    printf("%d\n", asd);
    return 0;
}

prim 算法链式前向星存图

//prim
#include <bits/stdc++.h>
using namespace std;
int n, m, minn[5010], asd, x, y, z, tot, head[5010];
bool vis[5010];
struct node
{
    int to, dis, nex;
}e[400010];
void add(int u, int v, int w)
{
    tot++;
    e[tot].to=v;
    e[tot].dis=w;
    e[tot].nex=head[u];
    head[u]=tot;
}
int main()
{
    scanf("%d %d", &n, &m);
    memset(minn, 0x3f, sizeof(minn));
    while(m--){
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
    }
    minn[1]=0;
    for(int i=1; i<=n; ++i){
        int minid=0;
        int minedge=0x3f3f3f3f;
        for(int j=1; j<=n; ++j){
            if(!vis[j] && minn[j]<minedge){
                minid=j;
                minedge=minn[j];
            }
        }
        vis[minid]=true;
        for(int j=head[minid]; j; j=e[j].nex){
            int v=e[j].to;
            int w=e[j].dis;
            //必须确保j是没加到生成树的点,才可更新 minn 值
            if(!vis[v]){
                minn[v]=min(minn[v], w);
            }
        }
    }
    for(int i=1; i<=n; ++i){
        if(minn[i]==0x3f3f3f3f){
            puts("orz");
            return 0;
        }
    }
    for(int i=1; i<=n; ++i){
        asd+=minn[i];
    }
    printf("%d\n", asd);
    return 0;
}

prim堆优化版本

//prim 堆优化版本
#include <bits/stdc++.h>
using namespace std;
int n, m, minn[5010], asd, x, y, z, tot, head[5010];
bool vis[5010];
//定义优先队列里的元素
struct Node
{
    int id, dis;
}edge;
priority_queue<Node> q;
//运算符重载
bool operator <(Node a, Node b)
{
    return a.dis>b.dis;
}
//链式前向星
struct node
{
    int to, dis, nex;
}e[400010];
//链式前向星建边
void add(int u, int v, int w)
{
    tot++;
    e[tot].to=v;
    e[tot].dis=w;
    e[tot].nex=head[u];
    head[u]=tot;
}
//prim堆优化版
void prim()
{
    //默认1号点开始
    edge.id=1;
    edge.dis=0;
    minn[1]=0;
    q.push(edge);
    while(!q.empty()){
        edge=q.top();
        q.pop();    //lg的复杂度
        int u=edge.id;
        if(vis[u]){ //优化复杂度
            continue;
        }
        vis[u]=true;    //点u加到生成树中
        //一人得道,鸡犬升天
        for(int i=head[u]; i; i=e[i].nex){
            int v=e[i].to;
            int w=e[i].dis;
            //必须确保v是没加到生成树的点,才可能更新 minn 值
            if(!vis[v] && w<minn[v]){
                minn[v]=w;
                edge.id=v;
                edge.dis=w;
                q.push(edge);
            }
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    memset(minn, 0x3f, sizeof(minn));
    while(m--){
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
    }
    prim();
    for(int i=1; i<=n; ++i){
        asd+=minn[i];
        if(minn[i]==0x3f3f3f3f){
            puts("orz");
            return 0;
        }
    }
    printf("%d\n", asd);
    return 0;
}

kruskal算法

//kruskal
#include <bits/stdc++.h>
using namespace std;

int n, m, x, y, fx, fy, cnt, ans;
int fa[5001];
struct edge
{
	int u, v, w;
}e[200000];
bool cmp(edge x, edge y)
{
	return x.w<y.w;
}
int find(int a)
{
	if(fa[a]==a)	return a;
	else return fa[a]=find(fa[a]);	
} 
int main()
{
	cin >> n >> m;
	for(int i=1; i<=n; i++)		fa[i]=i;
	for(int i=0; i<m; i++){
		cin >> e[i].u >> e[i].v >> e[i].w;
	}
	sort(e, e+m, cmp);
	for(int i=0; i<m; i++){
		fx=find(e[i].u);
		fy=find(e[i].v);
		if(fx!=fy){
			cnt++;
			ans+=e[i].w;
			fa[fx]=fy;
			if(cnt==n-1)	
				break;
		}
	}
	if(cnt==n-1)
		cout << ans;
	else	
		cout << "orz";	
	return 0; 
} 

P2872 [USACO07DEC]Building Roads S

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, fa[1010], total;
double x[1010], y[1010], ans;
struct node
{
	int from, to;
	double dis;
}edge[1000010];
bool cmp(node a, node b)
{
	return a.dis<b.dis;
}
int find(int a)
{
	if(a!=fa[a]){
		return fa[a]=find(fa[a]);
	}
	else{
		return fa[a];
	}
}
void merge(int a, int b)
{
	int u=find(a);
	int v=find(b);
	if(u!=v){
		fa[u]=v;
	}
}
double cal(int i, int j)
{
	return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		fa[i]=i;
		scanf("%lf %lf", &x[i], &y[i]);
	}
	for(int i=1; i<=m; ++i){
		scanf("%d %d", &u, &v);
		if(find(u)!=find(v)){
			merge(u, v);
		}
	}
	for(int i=1; i<n; ++i){
		for(int j=i+1; j<=n; ++j){
			total++;
			edge[total].from=i;
			edge[total].to=j;
			edge[total].dis=cal(i, j);
		}
	}
	sort(edge+1, edge+total+1, cmp);
	for(int i=1; i<=total; ++i){
		u=edge[i].from;
		v=edge[i].to;
		if(find(u)!=find(v)){
			merge(u, v);
			ans+=edge[i].dis;
		}
	}
	printf("%.2lf", ans);
	return 0;
}

P2121 拆地毯

#include <bits/stdc++.h>
using namespace std;
int n, m, k, u, v, fa[100010], cnt, ans;
struct node
{
	int from, to;
	int w;
}edge[100010];
bool cmp(node a, node b)
{
	return a.w>b.w;
}
int find(int a)
{
	if(a!=fa[a]){
		return fa[a]=find(fa[a]);
	}
	else{
		return fa[a];
	}
}
void merge(int a, int b)
{
	int u=find(a);
	int v=find(b);
	if(u!=v){
		fa[u]=v;
	}
}
int main()
{
	scanf("%d %d %d", &n, &m, &k);
	for(int i=1; i<=n; ++i){
		fa[i]=i;
	}
	for(int i=1; i<=m; ++i){
		scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].w);
	}
	sort(edge+1, edge+m+1, cmp);
	for(int i=1; i<=m; ++i){
		u=edge[i].from;
		v=edge[i].to;
		if(find(u)!=find(v)){
			merge(u, v);
			ans+=edge[i].w;
			cnt++;
			if(cnt==k){
				printf("%d", ans);
				return 0;
			}
		}
	}
	return 0;
}

P1195 口袋的天空

#include <bits/stdc++.h>
using namespace std;
int n, m, k, u, v, fa[1010], cnt, ans;
struct node
{
	int from, to;
	int dis;
}edge[10010];
bool cmp(node a, node b)
{
	return a.dis<b.dis;
}
int find(int a)
{
	if(a!=fa[a]){
		return fa[a]=find(fa[a]);
	}
	else{
		return fa[a];
	}
}
void merge(int a, int b)
{
	int u=find(a);
	int v=find(b);
	if(u!=v){
		fa[u]=v;
	}
}
int main()
{
	scanf("%d %d %d", &n, &m, &k);
	for(int i=1; i<=n; ++i){
		fa[i]=i;
	}
	for(int i=1; i<=m; ++i){
		scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].dis);
	}
	sort(edge+1, edge+m+1, cmp);
	for(int i=1; i<=m; ++i){
		u=edge[i].from;
		v=edge[i].to;
		if(find(u)!=find(v)){
			merge(u, v);
			ans+=edge[i].dis;
			cnt++;
			if(cnt+k==n){
				printf("%d", ans);
				return 0;
			}
		}
	}
	printf("No Answer");
	return 0;
}

P1536 村村通

#include <bits/stdc++.h>
using namespace std;
int n, m, ans, tot, fa[1010], cnt;
bool connect[1010][1010];
struct node
{
	int from, to;
}edge[500010];
void init()
{
	memset(connect, 0, sizeof(connect));
	tot=0;
	cnt=0;
	for(int i=1; i<=n; ++i){
		fa[i]=i;
	}
}
int find(int x)
{
	if(fa[x]!=x){
		return fa[x]=find(fa[x]);
	}
	else return x;
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	if(fu!=fv){
		fa[fu]=fv;
	}
}
int main()
{
	while(scanf("%d", &n) && n!=0){
		int u, v;
		scanf("%d", &m);
		init();
		while(m--){
			scanf("%d %d", &u, &v);
			if(u!=v && !connect[u][v]){
				connect[u][v]=connect[v][u]=true;
				tot++;
				edge[tot].from=u;
				edge[tot].to=v;
			}
		}
		for(int i=1; i<=tot; ++i){
			u=edge[i].from;
			v=edge[i].to;
			int fu=find(u);
			int fv=find(v);
			if(fu!=fv){
				cnt++;
				merge(fu, fv);
			}
		}
		if(cnt>=n-1){
			printf("0\n");
		}
		else{
			printf("%d\n", n-1-cnt);
		} 
	}
	return 0;
} 

P4047 [JSOI2010]部落划分

#include <bits/stdc++.h>
using namespace std;
int n, k, cnt, fa[1010], numk;
struct edge{
	int u, v;
	double dis;	
}e[1000000];
struct node
{
	double x, y;
}a[1010];
double cal(int u, int v)
{
	return sqrt((a[u].x-a[v].x)*(a[u].x-a[v].x)+(a[u].y-a[v].y)*(a[u].y-a[v].y));
}
//从小到大排序 
bool cmp(edge x, edge y)
{
	return x.dis<y.dis;
}
int find(int x)
{
	if(x==fa[x]){
		return x;
	}
	return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	if(fu==fv){
		return;
	}
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &k);
	for(int i=1; i<=n; ++i){
		scanf("%lf %lf", &a[i].x, &a[i].y);
		fa[i]=i;
	}
	for(int i=1; i<=n; ++i){		//将每条边都存起来 
		for(int j=i+1; j<=n; ++j){
			cnt++;
			e[cnt].u=i;
			e[cnt].v=j;
			e[cnt].dis=cal(i, j);
		}
	}
	sort(e+1, e+cnt+1, cmp);		//根据边权从小到大排序 
	numk=n;
	for(int i=1; i<=cnt; ++i){		//从最近的野人开始依次枚举 
		int fu=find(e[i].u);
		int fv=find(e[i].v);
		if(fu!=fv){					//如果这两个野人没在同一个部落, 则需要合并	
			merge(fu, fv);	
			numk--;						//没连一条边, 则部落数减1 
			if(numk==k){				//当部落数恰好为k时, 则不能再连边了 
				for(int j=i+1; j<=cnt; ++j){	//找距离最近的部落 
					fu=find(e[j].u);
					fv=find(e[j].v);
					if(fu!=fv){				//如果不在同一部落, 则找到答案 
						printf("%.2lf\n", e[j].dis);
						return 0;	
					}
				}
			}
		}
	}		
	return 0;
}

P2504 [HAOI2006]聪明的猴子

#include <bits/stdc++.h>
using namespace std;
int n, m, a[505], ans;
double g[1005][1005], flag;
bool vis[1005];
double minn[1005];
struct node{
	double x,y;
}site[1005];
double cal(int u, int v)
{
	return sqrt((site[u].x-site[v].x)*(site[u].x-site[v].x) + (site[u].y-site[v].y)*(site[u].y-site[v].y));
}
int main()
{
	cin >> m;	//表示猴子的个数
	for(int i=1; i<=m; ++i){
		cin >> a[i];	//依次表示猴子的最大跳跃距离
	}
	cin >> n;	//表示树的总棵数
	for(int i=1; i<=n; ++i){
		cin >> site[i].x >> site[i].y;
		minn[i]=1000000000;
	}
	for(int i=1; i<=n; ++i){
		for(int j=i+1; j<=n; ++j){
			g[i][j]=g[j][i]=cal(i, j);
		}
		minn[i]=min(minn[i], g[i][1]);
	}
	vis[1]=true;
	minn[1]=0;
	for(int i=1; i<=n; ++i){
	 	flag=1000000000;
		int u;
		for(int j=1; j<=n; ++j){
			if(minn[j]<flag && vis[j]==false){
				u=j;
				flag=minn[j];
			}
		}
		vis[u]=true;
		for(int j=1; j<=n; ++j){
			if(g[u][j]<minn[j] && vis[j]==false){
				minn[j]=g[u][j];
			}
		}
	}
	flag=0;
	for(int i=2; i<=n; ++i){
		flag=max(flag, minn[i]);
	}
	for(int i=1; i<=m; ++i){
		if(a[i]>=flag)	ans++;
	}
	cout << ans;
	return 0;
}

T112649 走廊泼水节

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int fat[6001], n, T, ans, i;
int num[6001];//记录并查集 集合里点的数量
void init(){
    for(int i=0;i<6001;i++){
        fat[i]=i;
        num[i]=1;
    }
    ans=0;
}
int father(int x)
{
	if(fat[x]!=x)
		fat[x]=father(fat[x]);
	return fat[x];
}
struct Node{
    int a,b,c;
}a[6001];
int cmp(Node a,Node b){
    return a.c<b.c;
}
int main(){
    cin >> T;
    while(T--){
        init();
        cin >> n;
        for(i=0; i<n-1; i++){
            cin >> a[i].a >> a[i].b >> a[i].c;
        }
        sort(a, a+n-1, cmp);
        for(i=0; i<n-1; i++){
            int x=father(a[i].a);
            int y=father(a[i].b);
            if(x==y)continue;
            ans+=(num[x]*num[y]-1)*(a[i].c+1);
            fat[x]=y;
            num[y]+=num[x];
        }
        cout << ans << endl;
    }
    return 0;
}

P1991 无线通讯网(好题)

#include <bits/stdc++.h>
using namespace std;
//s可安装的卫星电话的哨所数, p边防哨所的总数 
int s, p, x[510], y[510], total, fa[510], cnt;	
struct node
{
	int u, v;
	double dis;	
}edge[250010];
double cal(int i, int j)
{
	return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
bool cmp(node x, node y)	//根据距离从小到大排序 
{
	return x.dis<y.dis;
}
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int a, int b)
{
	int u=find(a);
	int v=find(b);
	if(u==v)	return;
	fa[u]=v;
}
int main()
{
	scanf("%d %d", &s, &p);
	for(int i=1; i<=p; ++i){
		fa[i]=i;
		scanf("%d %d", &x[i], &y[i]);
	} 
	if(s>=p){
		printf("0.00");
		return 0;
	} 
	for(int i=1; i<p; ++i){
		for(int j=i+1; j<=p; ++j){
			total++;
			edge[total].u=i;
			edge[total].v=j;
			edge[total].dis=cal(i, j);
		}
	}
	sort(edge+1, edge+total+1, cmp);
	//现在总共有s个点安装了卫星电话, 还有p-s个点没有加入网络中,
	//把装了卫星电话的哨所看出一个点, 相当于p-s+1个点求最小生成树, 需要连p-s条边, 
	//要想求最小的收发器通话距离 D, 那么就必须先满足最小生成树, 
	//满足了最小生成树后, 不用管卫星电话怎么设置, 生成树中第p-s条边的长度就是答案。
	//而不是先按照最大边先设卫星电话, 再求最小生成树, 这样可能会影响答案 
	for(int i=1; i<=total; ++i){
		int asd1, asd2;
		asd1=edge[i].u;
		asd2=edge[i].v;
		int fa1=find(asd1);
		int fa2=find(asd2);
		if(fa1!=fa2){
			merge(fa1, fa2);
			cnt++;
		}
		if(cnt==p-s){
			printf("%.2lf", edge[i].dis);
			return 0;
		}
	}
	return 0;
}

P1194 买礼物

#include <bits/stdc++.h>
using namespace std;
int n, m, x, price, total, fa[510], asd1, asd2, cnt, ans;
//边 
struct node
{
	int u, v, w;
}e[130000];
//根据价格从小到大排序 
bool cmp(node x, node y)
{
	return x.w<y.w;
}
//并查集查找祖先 
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
//并查集合并 
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	if(fu!=fv){
		fa[fu]=fv;
	}
}
int main()
{
	scanf("%d %d", &price, &n);
	for(int i=1; i<=n; ++i){
		fa[i]=i;	//并查集初始化 
		for(int j=1; j<=n; ++j){
			scanf("%d", &x);
			if(j>i){	//只存一条边就行了 
				if(x>price || x==0){ 	//太贵或者没有优惠, 按原价来 
					x=price;
				}
				total++;
				e[total].u=i;
				e[total].v=j;
				e[total].w=x;
			}
		}
	}
	sort(e+1, e+total+1, cmp);
	//Kruskal 
	for(int i=1; i<=total; ++i){	//枚举边 
		asd1=e[i].u;	//边的两端点 
		asd2=e[i].v;	//边的两端点
		//如果这两点没有连通起来 
		if(find(asd1)!=find(asd2)){
			cnt++;
			ans+=e[i].w;
			merge(asd1, asd2);	//连 
			if(cnt==n-1){		//全部连起来后 
				break;
			}
		}
	}
	ans+=price;
	printf("%d", ans);
	return 0;
}

贡献两组数据

P1194_1.in

3 4
0 1 0 0
1 0 0 0
0 0 0 0
0 0 0 0

P1194_1.out

10

P1194_4.in

800 7
0 104 999 196 952 882 800
104 0 262 0 440 475 261
999 262 0 143 613 789 456
196 0 143 0 392 316 458
952 440 613 392 0 966 130
882 475 789 316 966 0 882
800 261 456 458 130 882 0

P1194_4.out

1950

P2212 [USACO14MAR]Watering the Fields S

#include <bits/stdc++.h>
using namespace std;
const int N=2010, M=2000010;
int n, c, fa[N], tot, cnt, ans;
struct node1
{
	int x, y;
}point[N];
struct node
{
	int u, v;
	int dis;
}e[M];
double cal(int u, int v)
{
	return (point[u].x-point[v].x)*(point[u].x-point[v].x)+(point[u].y-point[v].y)*(point[u].y-point[v].y);
}
bool cmp(node x, node y)
{
	return x.dis<y.dis;
}
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fa[fv];
}
int main()
{
	scanf("%d %d", &n, &c);
	for(int i=1; i<=n; ++i){
		scanf("%d %d", &point[i].x, &point[i].y);
		fa[i]=i; 
	}
	for(int i=1; i<n; ++i){
		for(int j=i+1; j<=n; ++j){
			tot++;
			e[tot].u=i;
			e[tot].v=j;
			e[tot].dis=cal(i, j);
		}
	}
	sort(e+1, e+tot+1, cmp);
	for(int i=1; i<=tot; ++i){
		if(e[i].dis>=c){
			if(find(e[i].u)!=find(e[i].v)){
				ans+=e[i].dis;
				cnt++;
				merge(e[i].u, e[i].v);
				if(cnt==n-1){
					break;
				}
			}
		}
	}
	if(cnt==n-1){
		printf("%d", ans);
	}
	else{
		printf("-1");
	}
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ypeijasd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值