最小生成树

acwing 1140《信息学奥赛一本通》最短网络

#include<bits/stdc++.h>
using namespace std;
#define N 110
typedef long long ll;
ll a[N][N];
ll dist[N];
bool vis[N];
int n;
ll prim(){
    int start=1;
    ll res=0;
    dist[1]=0;
    vis[1]=1;
    //要先将一号节点加入集合
    for( int i=1;i<=n-1;i++){ 注意这里是n-1
        for( int j=1;j<=n;j++){
            dist[j]=min(dist[j],a[start][j]);
        }
        int maxx=0x7fffffff;
        for( int j=1;j<=n;j++){
            if(dist[j]<maxx&&vis[j]==0){
                maxx=dist[j];
                start=j;
            }
        }
        vis[start]=1;
        res+=dist[start];
    }  
    return res;
}
int main(){
    cin>>n;
    memset(a,-1,sizeof(a));
    memset(dist,0x3f,sizeof(dist));
    for( int i=1;i<=n;i++){
        for( int j=1;j<=n;j++){
            cin>>a[i][j];
            a[j][i]=a[i][j];
        }
    }
    cout<<prim();
}

acwing 1141局域网 《信息学奥赛一本通》
先计算所有边的和sum再减去最小生成树的和就是答案
本题中,图有可能不连通,再使用最小生成树时,如果下一个点的距离是无穷大,就赋为0,重新开一个树。

#include<bits/stdc++.h>
using namespace std;
#define N 110
#define M 410   这里注意开两倍,是双向边
typedef long long ll;
int w[M],to[M],ne[M],h[N];
int cnt;
int vis[N],dist[N];
int n,k;
void add(int a,int b,int c){
    w[cnt]=c;to[cnt]=b;ne[cnt]=h[a];h[a]=cnt++;
}

ll prim(){
    int start=1;
    vis[1]=1;
    dist[1]=0;
    ll res=0;
    for( int i=1;i<n;i++){
        for(int j=h[start];j!=-1;j=ne[j]){
            int t=to[j];
            dist[t]=min(w[j],dist[t]);
        }
        int maxx=0x7fffffff;
        for( int j=1;j<=n;j++){
            if(maxx>dist[j]&&vis[j]==0){
                start=j;
                maxx=dist[j];
            }
        }
        if(dist[start]==dist[105]){
            dist[start]=0;
        }
        res+=dist[start];
        vis[start]=1;
    }
    
    return res;
}
int main(){
    cin>>n>>k;
    memset(dist,0x3f,sizeof(dist));
    memset(h,-1,sizeof(h));
    ll sum=0;
    for( int i=1;i<=k;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
        sum+=c;
    }
    cout<<sum-prim();
}

acwing 1142 《信息学奥赛一本通》 繁忙的都市
二分+最小生成树
城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。
城市C的道路是这样分布的:
城市中有 n 个交叉路口,编号是 1∼n,有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路相连接。
这些道路是 双向 的,且把所有的交叉路口直接或间接的连接起来了。
每条道路都有一个分值,分值越小表示这个道路越繁忙,越需要进行改造。
但是市政府的资金有限,市长希望进行改造的道路越少越好,于是他提出下面的要求:
1.改造的那些道路能够把所有的交叉路口直接或间接的连通起来。
2.在满足要求1的情况下,改造的道路尽量少。
3.在满足要求1、2的情况下,改造的那些道路中分值最大值尽量小。
作为市规划局的你,应当作出最佳的决策,选择那些道路应当被修建。
输入格式
第一行有两个整数 n,m 表示城市有 n 个交叉路口,m 条道路。
接下来 m 行是对每条道路的描述,每行包含三个整数u,v,c 表示交叉路口 u 和 v 之间有道路相连,分值为 c。
输出格式
两个整数 s,max,表示你选出了几条道路,分值最大的那条道路的分值是多少。
数据范围
1≤n≤300,
1≤m≤8000,
1≤c≤10000
输入样例:
4 5
1 2 3
1 4 5
2 4 7
2 3 6
3 4 8
输出样例:
3 6

看到题目中有最大值最小,所以使用二分法,下面是二分法的设计方法
在这里插入图片描述
下面是代码:

#include<bits/stdc++.h>
using namespace std;
#define N 310
int n,m;
int mp[N][N];
int dist[N];
int vis[N];
int check(int bound ){
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    int res=0;
    dist[1]=0;
    vis[1]=1;
    int start=1;
    for( int j=1;j<n;j++){
        for( int i=1;i<=n;i++){
            if(mp[start][i]==-1) continue;
            int cost;
            if(mp[start][i]<=bound) cost=0;
            else  cost=1;
            dist[i]=min( cost , dist[i]);
        }
        int maxx=0x7fffffff;
        for( int i=1;i<=n;i++){
            if(vis[i]==0&&dist[i]<maxx){
                maxx=dist[i];
                start=i;
            }
        }
        vis[start]=1;
        res+=dist[start];
    }
    // cout<<res<<" ";
    return res;
}
int main(){
    cin>>n>>m;
    memset(mp,-1,sizeof(mp));
    for( int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        mp[a][b]=c;
        mp[b][a]=mp[a][b];
    }
    int l=0;
    int r=1e4+1;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid)<=0) r=mid;
        else l=mid+1;
    }
    cout<<n-1<<" "<<l<<endl;
}

acwing 1143 《信息学奥赛一本通》联络员

#include<bits/stdc++.h>
using namespace std;
#define N 2010
#define M 20010
typedef long long ll;
int w[M],to[M],ne[M],h[N];
int cnt,m,n;
void add( int a,int b,int c){
    w[cnt]=c;to[cnt]=b;ne[cnt]=h[a];h[a]=cnt++;
}
int vis[N];
int dist[N];
ll prim(){
    memset(dist,0x3f,sizeof(dist));
    ll res=0;
    int st=1;
    vis[1]=1;
    dist[1]=0;
    int t=n-1;
    while(t--){
        for( int i=h[st];i!=-1;i=ne[i]){
            int j=to[i];
            dist[j]=min(dist[j],w[i]);
        }
        int maxx=0x7fffffff;
        for( int i=1;i<=n;i++){
            if(dist[i]<maxx&&vis[i]==0){
                maxx=dist[i];
                st=i;
            }
        }
        vis[st]=1;
        res+=dist[st];
    }
    return res;
}
int main(){
    cin>>n>>m;
    ll sum=0;//表示必选边的权重和
    memset(h,-1,sizeof(h));
    for( int i=1;i<=m;i++){
        int a,b,c,p;
        cin>>p>>a>>b>>c;
        if(p==1){
            add(a,b,0);
            add(b,a,0);
            sum+=c;
        }
        else {
            add(a,b,c);
            add(b,a,c);
        }
    }
    cout<<sum+prim();
    return 0;
    
}

acwing 1144 /《信息学奥赛一本通》连接格点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值