最小生成树 水题集

先说求无向图最小生成树的两种算法:

①Prim算法

1.初始时两个集合V,E,初始时V为空,E是所有点的集合。

2.以图中任意一个点为起始点,把这点加入集合V,然后从集合E中删除,然后更新这个点到其它点的距离,如果更新不到的点,那么之间的距离为无穷大。

3.然后挑选离起始点最近的点,把这点加入集合V,然后从集合E中删除(有多个的话任选一个),然后更新  新加入的点  到其它点的距离,然后再找出一个距离这个集合最近的点(这个距离最近的点可能离起始点最近)。

4.然后重复上面的步骤,直至路被更新完或E为空,如果E不为空,说明图不连通。

②克鲁斯卡尔算法

1.将两点之间的距离排序

2.每次从中选出距离最短的两点,判断这两点所在的集合是否联通。

3.如果联通,继续步骤2;如果不连通,将两点合并为一个集合,继续步骤2

4.直至所有点都联通,或路径判断完毕

其中稠密图适合用prim,稀疏图适合用克鲁斯卡尔。

两个算法的基础都是贪心算法,下面的水题,练练手。。。

 

Hdu  1102  Constructing Roads

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1102

题目大意:给你一个矩阵map,其中map[i][j]表示从i到j的花费,给你多次询问,输出询问的两点之间联通的最小花费

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

#define Maxint 0x3f3f3f3f
#define N 110

int map[N][N],visit[N],low[N];
int n,m;
int prim()
{
    int Min,sum=0,pos,i,j;
    memset(visit,0,sizeof(visit));
    visit[1]=1;pos=1;
    for(int i=1;i<=n;i++)
        if(i!=pos)
        low[i]=map[pos][i];
    for(int i=1;i<n;i++)
    {
        Min=Maxint;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]<Min)
            {
                Min=low[j];
                pos=j;
            }
        sum+=Min;
        visit[pos]=1;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]>map[pos][j])
            low[j]=map[pos][j];
    }
    return sum;
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
        {scanf("%d",&map[i][j]);map[j][i]=map[i][j];}
        scanf("%d",&m);
        int a,b;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            map[a][b]=map[b][a]=0;
        }
        printf("%d\n",prim());
    }
    return 0;
}


Hdu  1863  畅通工程

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1863

题目大意:中文题,不解释

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

#define Maxint 0x3f3f3f3f
#define N 110
int n,m,rank,map[N][N],sum,f[N];
//克鲁斯卡尔
struct point{
int x,y,dis;
point():x(0),y(0),dis(0){}//结构体的初始化
}s[N];
bool cmp(point a,point b)
{
    return a.dis<b.dis;
}
void init()
{
    sum=0;
    rank=m;
    for(int i=0;i<=N;i++)
        f[i]=i;
}
int find(int x)
{
    if(x!=f[x]) f[x]=find(f[x]);
    return f[x];
}
int merge(point a)
{
    int fx=find(a.x);
    int fy=find(a.y);
    if(fx!=fy)
    {
        f[fx]=fy;
        sum+=a.dis;
        rank--;
    }
    return rank;
}
int main()
{
    while(scanf("%d%d",&n,&m)&&n)
    {
        init();
        for(int i=0; i<n; i++)
            scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].dis);
        sort(s,s+n,cmp);
        int i;
        for(i=0;i<n;i++)
        {
            if(merge(s[i])==1)
            {printf("%d\n",sum);break;}
        }
        if(rank>1)
            printf("?\n");
    }
    return 0;
}

 

Hdu  1875   畅通工程再续

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1875

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

#define Maxint 0x3f3f3f3f
#define N 110

//prim
int visit[N],n;
double low[N],map[N][N];
struct point{
int x;
int y;
}s[N];
void init()//初始化
{
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
        {
            if(i==j) map[i][j]=0;
            else map[i][j]=Maxint;
        }
}
double prim()
{
    double Min,sum=0;
    int pos,i,j;
    memset(visit,0,sizeof(visit));
    visit[0]=1;pos=0;
    for(int i=0;i<n;i++)
        if(i!=pos)
        low[i]=map[pos][i];
    for(int i=1;i<n;i++)
    {
        Min=Maxint;
        for(int j=0;j<n;j++)
            if(visit[j]==0&&low[j]<Min)
            {
                Min=low[j];
                pos=j;
            }
        if(Min==Maxint) return -1;
        sum+=Min;
        visit[pos]=1;
        for(int j=0;j<n;j++)
            if(visit[j]==0&&low[j]>map[pos][j])
            low[j]=map[pos][j];
    }
    return sum;
}

double S(point a,point b)//两坐标之间距离
{
    double len;
    len=sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
    return len;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=0;i<n;i++)
            scanf("%d%d",&s[i].x,&s[i].y);
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
            {
                double len=S(s[i],s[j]);
                if(len>=10&&len<=1000)
                    map[i][j]=map[j][i]=len*100;
            }
        double ans=prim();
        if(ans==-1) printf("oh!\n");
        else printf("%.1lf\n",ans);
    }
    return 0;
}


 

Hdu  1879  继续畅通工程

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1879

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

#define Maxint 0x3f3f3f3f
#define N 110

//prim
struct point{
int x,y,dis,f;
}s[100005];      //注意s的范围
int visit[N],n,low[N],map[N][N];
void init()
{
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
        {
            if(i==j) map[i][j]=0;
            else map[i][j]=Maxint;
        }
}
int prim()
{
    int Min,sum=0;
    int pos,i,j;
    memset(visit,0,sizeof(visit));
    visit[1]=1;pos=1;
    for(int i=1;i<=n;i++)
        if(i!=pos)
        low[i]=map[pos][i];
    for(int i=1;i<n;i++)
    {
        Min=Maxint;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]<Min)
            {
                Min=low[j];
                pos=j;
            }
        sum+=Min;
        visit[pos]=1;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]>map[pos][j])
            low[j]=map[pos][j];
    }
    return sum;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        int a=n*(n-1)/2;
        init();
        for(int i=0; i<a; i++)
        {
            scanf("%d%d%d%d",&s[i].x,&s[i].y,&s[i].dis,&s[i].f);
            if(s[i].f==1) map[s[i].x][s[i].y]=map[s[i].y][s[i].x]=0;
            else map[s[i].x][s[i].y]=map[s[i].y][s[i].x]=s[i].dis;
        }
        printf("%d\n",prim());
    }
    return 0;
}


Hdu  3371  Connect the Cities

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3371

题目大意:有N个城市,M条路,并且在输完路之后,再输入K个城市群,每个城市群里有P个城市相连,这P个城市之间的花费为0,求所有城市相连的最小花费,不能全部相连输出-1.

注意:代码用G++超时,用C++ 500ms AC

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

#define Maxint 0x3f3f3f3f
#define N 510

int visit[N],n,low[N],map[N][N],v[N];
void init()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(i==j) map[i][j]=0;
            else map[i][j]=map[j][i]=Maxint;
        }
}
int prim()
{
    int Min,sum=0;
    int pos,i,j;
    memset(visit,0,sizeof(visit));
    visit[1]=1;pos=1;
    for(int i=1;i<=n;i++)
        if(i!=pos)
        low[i]=map[pos][i];
    for(int i=1;i<n;i++)
    {
        Min=Maxint;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]<Min)
            {
                Min=low[j];
                pos=j;
            }
        if(Min==Maxint) return -1;
        sum+=Min;
        visit[pos]=1;
        for(int j=1;j<=n;j++)
            if(visit[j]==0&&low[j]>map[pos][j])
            low[j]=map[pos][j];
    }
    return sum;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int m,k,num;
        scanf("%d%d%d",&n,&m,&k);
        init();
        int a,b,c;
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(map[a][b]>c) map[a][b]=c,map[b][a]=c;
        }
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&num);
            for(int j=0;j<num;j++)
                scanf("%d",&v[j]);
            for(int j=0;j<num;j++)
                for(int kk=j+1;kk<num;kk++)
                map[v[kk]][v[j]]=map[v[j]][v[kk]]=0;
        }
        printf("%d\n",prim());
    }
    return 0;
}


 

Hdu  3367  Pseudoforest  最大生成树

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3367

题目大意:将所有的点相连的最大花费,但相连的图中最多只有一个环。

代码如下:

//克鲁斯卡尔
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int f[N],visit[N];
int sum;

struct point{
int x,y,dis;
}s[100005];
bool cmp(point a,point b)
{
    return a.dis>b.dis;
}
void init()
{
    sum=0;
    memset(visit,0,sizeof(visit));
    for(int i=0;i<N;i++)
        f[i]=i;
}
int find(int x)
{
    if(x!=f[x]) f[x]=find(f[x]);
    return f[x];
}
void merge(point a)
{
    int fx=find(a.x);
    int fy=find(a.y);
    if(fx==fy&&visit[fx]==0)
    {
        sum+=a.dis;
        visit[fx]=1;
    }
    else
    {
        if(visit[fx]==0&&visit[fy]==0)
        {
            f[fx]=fy;
            sum+=a.dis;
        }
        else if(visit[fx]==0||visit[fy]==0)
        {
            visit[fx]=1;
            visit[fy]=1;
            f[fx]=fy;
            sum+=a.dis;
        }
    }
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        init();
        for(int i=0;i<m;i++)
            scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].dis);
        sort(s,s+m,cmp);
        for(int i=0;i<m;i++)
            merge(s[i]);
        printf("%d\n",sum);
    }
    return 0;
}

 

关于有向图的最小生成树,应该是最小树形图,会在接下来整理。。。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值