最小代价生成树

最小代价生成树:

针对图的概念:
1.生成树中的边的数量为节点数减1
2所有剩余边权加在一起最小的生成树

Kruskal算法:

1 使用并查集:
2 以边排序

//最小生成树模板
#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
    int s,e,v;
    bool operator<(const edge &b)const{
        return this->v<b.v;
    }
};
edge edg[200005];
int n,m,ans,my_union[5005],cnt;
void init(){
    for(int i=1;i<=n;i++){
        my_union[i]=i;
    }
}
int find_fa(int x){
    if(my_union[x]==x){
        return x;
    }
    return my_union[x]=find_fa(my_union[x]);//路径压缩
}
int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        cin>>edg[i].s>>edg[i].e>>edg[i].v;
    }
    sort(edg,edg+m);
    init();
    for(int i=0;i<m;i++){
        int s=edg[i].s,e=edg[i].e,v=edg[i].v;
        int fa=find_fa(s),fb=find_fa(e);
        if(fa!=fb){
            my_union[fa]=fb;
            ans+=v;
            cnt++;
            if(cnt==n-1){
                cout<<ans<<endl;
                return 0;
            }
        }
    }
    cout<<"orz"<<endl;
    return 0;
}

prim算法:

最小生成树不唯一

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
using namespace std;
struct node{
    int e,v;
    bool operator<(const node&b)const{
        return this->v>b.v;
    }
};
struct edge{
    int e,v,next;
};
edge edg[400005];
//mark 表示某点是否有连通
int n,m,head[5005],mark[5005],ans,cnt,edg_cnt,num[5005];
void add_edg(int a,int b,int c){
    edg[edg_cnt].e=b;
    edg[edg_cnt].v=c;
    edg[edg_cnt].next=head[a];
    head[a]=edg_cnt++;
}
int main(){
    memset(head,-1,sizeof(head));
    memset(num,0x3F,sizeof(num));
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add_edg(a,b,c);
        add_edg(b,a,c);
    }
    priority_queue<node>que;
    que.push((node){n/2,0});//可以随机选
    while(!que.empty()){
        node temp=que.top();
        que.pop();
        if(mark[temp.e]==1){
            continue;
        }
        ans+=temp.v;
        mark[temp.e]=1;
        cnt++;
        if(cnt==n){
            cout<<ans<<endl;
            return 0;
        }
        for(int i=head[temp.e];i!=-1;i=edg[i].next){
             int e=edg[i].e,v=edg[i].v;
             if(mark[e]==0&&num[e]>v){
                 que.push((node){e,v});
                 num[e]=v;//num[x]为连接到x的某条边的权值
             }
        }
    }
    cout<<"orz"<<endl;
    return 0;
}

题目描述
某国有n个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。

修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。

政府审批的规则如下:

(1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;

(2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请;

(3)其他情况的申请一律同意。

一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相:连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。

当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。

你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。

输入格式
第一行一个整数n,表示城市的数量。(n≤5000)

以下n行,每行两个整数x和y,表示一个城市的坐标。(-1000000≤x,y≤1000000)

输出格式
一个实数,四舍五入保留两位小数,表示公路总长。(保证有惟一解)

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
using namespace std;
struct node{
    int e;
    double v;
    bool operator<(const node&b)const{
        return this->v>b.v;
    }
};
int n,xy[5005][2],mark[5005],cnt;
double num[5005],ans;
double func(int a,int b){
    long long t1=xy[a][0]-xy[b][0];
    long long t2=xy[a][1]-xy[b][1];
    return sqrt(t1*t1+t2*t2);
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
       cin>>xy[i][0]>>xy[i][1];
       num[i]=999999999999999;
    }
    priority_queue<node>que;
    que.push((node){1,0});//可以随机选
    while(!que.empty()){
        node temp=que.top();
        que.pop();
        if(mark[temp.e]==1){
            continue;
        }
        ans+=temp.v;
        //mark[temp.e]=1;
        cnt++;
        mark[temp.e]=1;
        if(cnt==n){
            //cout<<ans<<endl;
            printf("%.2f\n",ans);
            return 0;
        }
        for(int i=1;i<=n;i++){
             //int e=edg[i].e,v=edg[i].v;
             if(mark[i]==0&&i!=temp.e){
                 double t=func(temp.e,i);
                 if(num[i]>t){
                     num[i]=t;
                     que.push((node){i,t});
                 }
             }
        }
    }
    //cout<<"orz"<<endl;
    return 0;
}

题目描述
国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;

每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。

任意两个配备了一条卫星电话线路的哨所(两边都ᤕ有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。

收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

输入格式
从 wireless.in 中输入数据第 1 行,2 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标(x, y),以 km 为单位。

输出格式
输出 wireless.out 中

第 1 行,1 个实数 D,表示无线电收发器的最小传输距离,精确到小数点后两位。

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<algorithm>
using namespace std;
struct edge{
    int s,e;
    double v;
};
bool cmp(const edge&a,const edge&b){
        return a.v<b.v;
}
edge edg[250005];
int k,n,my_union[505],xy[505][2],edg_cnt,cnt;
void init(){
    for(int i=1;i<=n;i++){
        my_union[i]=i;
    }
}
// int n,xy[5005][2],mark[5005],cnt;
// double num[5005],ans;
// double func(int a,int b){
//     long long t1=xy[a][0]-xy[b][0];
//     long long t2=xy[a][1]-xy[b][1];
//     return sqrt(t1*t1+t2*t2);
// }
int find_fa(int x){
    if(my_union[x]==x){
        return x;
    }
    return my_union[x]=find_fa(my_union[x]);
}
int main(){
    cin>>k>>n;
    init();
    for(int i=1;i<=n;i++){
       cin>>xy[i][0]>>xy[i][1];
       for(int j=1;j<i;j++){
           int t1=xy[i][0]-xy[j][0];
           int t2=xy[i][1]-xy[j][1];
           edg[edg_cnt].s=i;
           edg[edg_cnt].e=j;
           edg[edg_cnt++].v=sqrt(t1*t1+t2*t2);
       }
       //num[i]=999999999999999;
    }
    sort(edg,edg+edg_cnt,cmp);
    for(int i=0;i<edg_cnt;i++){
        int s=edg[i].s,e=edg[i].e;
        double v=edg[i].v;
        int fa=find_fa(s),fb=find_fa(e);
        if(fa!=fb){
            my_union[fa]=fb;
            cnt++;
            if(cnt==n-k){
                printf("%.2f\n",v);
                return 0;
            }
        }
    }
    //cout<<"orz"<<endl;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值