[kuangbin带你飞]专题六 最小生成树 题解+总结

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/JiangHxin/article/details/102156838

kuangbin带你飞:点击进入新世界
最小生成算法模板:点击进入新世界

总结:

本人算是初学者中的初学者,欢迎交流~
感觉这个专题的难度比最短路还简单…可能是错觉吧…都是入门题,加深对算法的理解。
kuangbin的题型:
A.模板题 参考1.2.4.9.10
B.考察并查集 参考3.6.8
C.次小生成树 参考5
D. k条边免费(题型有点像最短路的分层图)参考7

kuangbin之外:
最小生成树模板
次小生成树模板

1.Jungle Roads

原题链接:传送门

思路:

  1. 模板题,把字符转化成整型后基本跟模板一模一样。

    代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
int n,m,u,v,w,k;
char c,cc;
int f[manx];
struct node{
    int u,v,w;
}a[manx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n)
    {
        k=0;
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<n;i++)
        {
            cin>>c>>m;
            for(int i=1;i<=m;i++)
            {
                cin>>cc>>w;
                a[++k].u=c-'A'+1;
                a[k].v=cc-'A'+1;
                a[k].w=w;
            }
        }
        sort(a+1,a+1+k,cmp);
        ll ans=0;
        int total=1;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            ans+=a[i].w;
            f[u]=v;
            total++;
            if(total==n) break;
        }
        cout<<ans<<endl;
    }
    return 0;
}


2.Networking

原题链接:传送门

思路:

  1. 完完全全的模板题,按照最小生成树的思想写代码即可。

    代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
    int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n)
    {
        k=0;
        for(int i=1;i<=n;i++) f[i]=i;
        cin>>m;
        for(int i=1;i<=m;i++)
        {
            cin>>u>>v>>w;
            a[++k].u=u;
            a[k].v=v;
            a[k].w=w;
        }
        sort(a+1,a+1+k,cmp);
        ll ans=0;
        int total=1;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            ans+=a[i].w;
            f[u]=v;
            total++;
            if(total==n) break;
        }
        cout<<ans<<endl;
    }
    return 0;
}


3.Constructing Roads

原题链接:传送门

思路:

扫描二维码关注公众号,回复: 7629630 查看本文章
  1. 由于双向路,只需要记录一半的边就可以了。
  2. 常规最小生成树,只不过多了将部分点连接起来,可以用并查集完成这一份操作。
  3. 即便连接起来点原本的祖先是自己,将点连通的时候也需要先find(u),find(v),这一点可参考并查集的连通操作。

    代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
    int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n)
    {
        k=0;
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++){
                cin>>w;
                if(j>i)
                a[++k].u=i,a[k].v=j,a[k].w=w;
            }
        }
        cin>>m;
        ll ans=0;
        int total=1;
        for(int i=1;i<=m;i++){
            cin>>u>>v;
            u=find(u),v=find(v);
            if(u!=v){
                f[u]=v;
                total++;
            }
        }
        sort(a+1,a+1+k,cmp);
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            ans+=a[i].w;
            f[u]=v;
            total++;
            if(total==n) break;
        }
        cout<<ans<<endl;
    }
    return 0;
}


4.Agri-Net

原题链接:传送门

思路:

  1. 超级模板题,由于双向路,只需要记录一半的边就可以了。

    代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
    int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n)
    {
        k=0;
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++){
                cin>>w;
                if(j>i)
                a[++k].u=i,a[k].v=j,a[k].w=w;
            }
        }
        sort(a+1,a+1+k,cmp);
        ll ans=0;
        int total=1;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            ans+=a[i].w;
            f[u]=v;
            total++;
            if(total==n) break;
        }
        cout<<ans<<endl;
    }
    return 0;
}


5.The Unique MST

原题链接:传送门

思路:

  1. 次小生成树的模板题,详细可以看开头的模板,里面有详细的解释,如果有不明白的地方可以私信交流~

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx],d[manx][manx];
vector<int>p[manx];
struct node{
    int u,v,w;
    bool vis;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        k=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            p[i].clear();
            p[i].push_back(i);
            f[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            a[++k].u=u,a[k].v=v,a[k].w=w,a[i].vis=false;
        }
        sort(a+1,a+1+k,cmp);
        ll ans=0;
        int total=1;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            ans+=a[i].w;
            f[u]=v;
            a[i].vis=1;
            total++;
            int l1=p[u].size(),l2=p[v].size();
            for(int j=0;j<l1;j++)
                for(int k=0;k<l2;k++)
                    d[p[u][j]][p[v][k]]=d[p[v][k]][p[u][j]]=a[i].w;
            for(int j=0;j<l1;j++) p[v].push_back(p[u][j]);
            if(total==n) break;
        }
        ll res=inf;
        for(int i=1;i<=k;i++)
            if(!a[i].vis)
                res=min(res,ans+a[i].w-d[a[i].u][a[i].v]);
        if(res>ans) cout<<ans<<endl;
        else cout<<"Not Unique!"<<endl;
    }
    return 0;
}


6.Highways

原题链接:传送门

思路:

  1. 算是考察了并查集的连通操作,这道题的话因为是稠密图,用krus算法的话会超时,所以需要一些特别的优化技巧(剪枝技巧~ 详细见代码

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e4;
const int mamx=5e6+5000;
int n,m,u,v,w,k;
int x[manx],y[manx],f[manx];
struct node{
    int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    scanf("%d",&n);
    k=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x[i],&y[i]);
        f[i]=i;
    }
    scanf("%d",&m);
    int total=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        u=find(u),v=find(v);
        if(u!=v){ 
            f[u]=v;
            total++; //连通时直接计算最小生成树集合里面的点数
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            u=find(i),v=find(j);
            if(u==v) continue; //当边在最小生成树的集合里面跳过 
            w=(x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]);
            a[++k].u=i,a[k].v=j,a[k].w=w;
        }
    sort(a+1,a+1+k,cmp);
    for(int i=1;i<=k;i++)
    {
        u=find(a[i].u),v=find(a[i].v);
        if(u==v) continue;
        f[u]=v;
        total++;
        cout<<a[i].u<<" "<<a[i].v<<endl;
        if(total==n) break;
    }
    return 0;
}


7.Arctic Network

原题链接:传送门

思路:

  1. 这道题巨坑,卡了很久,原因在G++不支持%lf,如果G++提交请用%f,如果C++提交请用%lf 。
  2. 可选择k条边免费,即求最小生成树第n-k大边,因为kru算法的边是排序好的,因此只要把第n-k条加入最小生成树的边输出就好。

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3;
const int mamx=5e6+5000;
int n,m,u,v,w,k;
int x[manx],y[manx],f[manx];
struct node{
    int u,v;
    double w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&m);
        k=0;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x[i],&y[i]);
            f[i]=i;
        }
        for(int i=1;i<=m;i++)
            for(int j=i+1;j<=m;j++)
            {
                double z=sqrt((x[i]-x[j])*(x[i]-x[j])*1.0+(y[i]-y[j])*(y[i]-y[j])*1.0);
                a[++k].u=i,a[k].v=j,a[k].w=z;
            }
        sort(a+1,a+1+k,cmp);
        int total=1;
        double ans=0.00;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            f[u]=v;
            if(total==m-n){
                ans=a[i].w;
                break;
            }
            total++;
        }
        printf("%.2f\n",ans);
    }
    return 0;
}


8.Building a Space Station

原题链接:传送门

思路:

  1. 这道题同样需要注意:在G++不支持%lf,如果G++提交请用%f,如果C++提交请用%lf 。
  2. 在求两球之间的距离时,如果w<=0,则把这两个球之间的边加入最小生成树的集合中,其他没有什么坑点。

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
double x[manx],y[manx],z[manx],r[manx];
int f[manx];
struct node{
    int u,v;
    double w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n){
        k=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x[i],&y[i],&z[i],&r[i]);
            f[i]=i;
        }
        int total=1;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            {
                double w=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]))-r[i]-r[j];
                if(w>0)a[++k].u=i,a[k].v=j,a[k].w=w;
                else{
                    u=find(i),v=find(j);
                    if(u==v) continue;
                    f[u]=v;
                    total++;
                }
            }
        sort(a+1,a+1+k,cmp);
        double ans=0.000;
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            f[u]=v;
            ans+=a[i].w;
            total++;
            if(total==n) break;
        }
        printf("%.3lf\n",ans);
    }
    return 0;
}


9.还是畅通工程

原题链接:传送门

思路:

  1. 模板题。但是输入貌似很多,需要用scanf,不能用cin,不然会超时。

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
    int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    while(cin>>n&&n){
        k=0;
        m=n*(n-1)/2;
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
        for(int i=1;i<=n;i++) f[i]=i;
        int total=1;
        ll ans=0;
        sort(a+1,a+1+m,cmp);
        for(int i=1;i<=m;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            f[u]=v;
            ans+=a[i].w;
            total++;
            if(total==n) break;
        }
        printf("%d\n",ans);
    }
    return 0;
}


10.畅通工程再续

原题链接:传送门

思路:

  1. 模板题。
  2. 比普通的模板题多了x和y,需要把坐标化成距离,还有浮点的数据需要注意。

    代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int x[manx],y[manx];
int f[manx];
struct node{
    int u,v;
    double w;
}a[mamx];
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int find(int x)
{
    if(f[x]==x) return x;
    else return f[x]=find(f[x]);
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%d",&n);
        k=0;
        for(int i=1;i<=n;i++)
            scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            {
                double ww=sqrt((x[i]-x[j])*(x[i]-x[j])*1.000+(y[i]-y[j])*(y[i]-y[j]));
                if(ww>1000||ww<10) continue;
                a[++k].u=i,a[k].v=j,a[k].w=ww*100;
            }
        int total=1;
        double ans=0;
        sort(a+1,a+1+k,cmp);
        for(int i=1;i<=k;i++)
        {
            u=find(a[i].u),v=find(a[i].v);
            if(u==v) continue;
            f[u]=v;
            ans+=a[i].w;
            total++;
        }
        if(total==n) printf("%.1lf\n",ans);
        else puts("oh!");
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/JiangHxin/article/details/102156838