最小生成树 Kruskal 算法 简单题

生成树 无向连通图G的一个子图如果是一棵包含G的所有顶点的树

Kruskal算法 并查集的应用 

边的长度排序 选取当前最小距离的边 加入图中 看是否能够构成环(用并查集判断是否有环)  没有环就将边加入到图中 

ZOJ  1203  给出n个点的坐标  求连接n个点所需线路长度总和的最小值

除了要计算下两点之间的距离外 就是裸的Kruskal

贴个代码就好

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

struct edge
{
    int u,v;
    double len;
    bool operator <(const edge p)const
    {
        return len<p.len;
    }
};
edge e[5000];
double x[110],y[110];
double sum;
int n,m;
int parent[110];//根节点

double LEN(int i,int j)
{
    double l=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
    return l;
}

void init()
{
    for(int  i=0;i<n;i++)
        parent[i]=-1;
}
//并查集
int Find(int x)//       查找并返回根节点
{
    int p;
    for(p=x;parent[p]>=0;p=parent[p]);
    while(p!=x)//压缩路径
    {
        int tmp=parent[x];
        parent[x]=p;
        x=tmp;
    }
    return p;
}

void Union(int R1,int R2)//将两个不同集合的元素进行合并
{
    int r1=Find(R1); int r2=Find(R2);
    int tmp=parent[r1]+parent[r2]; //两个集合结点个数只喝(负数)
    if(parent[r1]>parent[r2]) //加权法则 将子节点更多的节点作为父节点
    {
        parent[r1]=r2;
        parent[r2]=tmp;
    }
    else
    {
        parent[r2]=r1;
        parent[r1]=tmp;
    }
}

void Kruskal()
{
    int num=0;  //已经选用边的个数
    int x,y;    //存储边的两个顶点
    init();
    for(int i=0;i<m;i++)
    {
        x=e[i].u; y=e[i].v;
        if(Find(x)!=Find(y))
        {
            sum+=e[i].len;
            num++;
            Union(x,y);
        }
        if(num>=(n-1)) break;
    }
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    int cs=1;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)  break;
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&x[i],&y[i]);
        int cnt=0;
        m=0;
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
        {
            e[cnt].u=i; e[cnt].v=j;
            e[cnt].len=LEN(i,j);
            cnt++;
            m++;
        }
        sort(e,e+m);
        sum=0.0;
        Kruskal();
        if(cs>1)  puts("");
        printf("Case #%d:\n",cs);
        printf("The minimal distance is: %.2lf\n",sum);
        cs++;
    }
    return 0;
}


ZOJ 1542

给出n个点 m条边的长度  求出连接n个点最短的长度和

输出选取边中节点的值

直接Kruskal算法就好   注意parent数组的初始化

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int n,m;
struct edge
{
    int u,v;
    int len;
    bool operator <(const edge p)const
    {
        return len<p.len;
    }
};
edge e[15010];
int parent[1010];
int note[1010];

void init()
{
    for(int i=0;i<=n;i++)
        parent[i]=-1;//注意 因为每个节点的值都是大于等于1小于等于n的 所有n要包括其中
}

int Find(int x)
{
    int p;
    for(p=x;parent[p]>=0;p=parent[p]);
    while(p!=x)
    {
        int tmp=parent[x];
        parent[x]=p;
        x=tmp;
    }
    return p;
}

void Union(int R1,int R2)
{
    int r1=Find(R1),r2=Find(R2);
    int tmp=parent[r1]+parent[r2];
    if(parent[r1]>parent[r2])
    {
        parent[r1]=r2;
        parent[r2]=tmp;
    }
    else
    {
        parent[r2]=r1;
        parent[r1]=tmp;
    }
}

void Kruskal()
{
    init();
    int mx=-1;
    int x,y;
    int cnt=0;
    for(int i=0;i<m;i++)
    {
        x=e[i].u; y=e[i].v;
        if(Find(x)!=Find(y))
        {
            mx=max(mx,e[i].len);
            note[cnt++]=i;
            Union(x,y);
        }
        if(cnt>=(n-1))
        {
            printf("%d\n",mx);
            break;
        }
    }
}

int main()
{
//freopen("ceshi.txt","r",stdin);

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<m;i++)
        {
            int x,y,l;
            scanf("%d%d%d",&x,&y,&l);
            e[i].u=x; e[i].v=y;
            e[i].len=l;
        }
        sort(e,e+m);
//        cout<<"whwhw"<<endl;
        Kruskal();
        printf("%d\n",n-1);
        for(int i=0;i<n-1;i++)
        {
            int tmp=note[i];
            printf("%d %d\n",e[tmp].u,e[tmp].v);
        }
    }
    return 0;
}


又水了两个题。。。

zoj 1406

没什么好说的  主要是读入的问题 

我一开始用 scanf 读入 因为里面有字符 有数字 有空格 换行 我就一个个的去读入 但是有问题 不造为什么

用cin直接读入就对了。。。郁闷。。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;
int n;
struct edge
{
    int u,v;
    int len;
    bool operator<(const edge p)const
    {
        return len<p.len;
    }
};
edge e[80];
int parent[27];
int res;
int cnt;//边的条数

void init()
{
    for(int i=0;i<27;i++)
        parent[i]=-1;
}

int Find(int x)
{
    int p;
    for(p=x;parent[p]>=0;p=parent[p]);
    while(p!=x)
    {
        int tmp=parent[x];
        parent[x]=p;
        x=tmp;
    }
    return p;
}

void Union(int R1,int R2)
{
    int r1=Find(R1),r2=Find(R2);
    int tmp=parent[r1]+parent[r2];
    if(parent[r1]>parent[r2])
    {
        parent[r1]=r2;
        parent[r2]=tmp;
    }
    else
    {
        parent[r2]=r1;
        parent[r1]=tmp;
    }
}

void Kruskal()
{
    init();
    int num=0;
    for(int i=0;i<cnt;i++)
    {
        int x,y;
        x=e[i].u; y=e[i].v;
        if(Find(x)!=Find(y))
        {
            res+=e[i].len;
            num++;
            Union(x,y);
        }
        if(num>=n-1)  break;
    }
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    while(cin>>n)
    {
        if(n==0)  break;
        cnt=0;
        for(int i=0;i<n-1;i++)
        {
            char a,b;
            int x,y,l;
            cin>>a>>y;
            x=a-'A';//从0开始
            for(int j=0;j<y;j++)
            {
                cin>>b>>l;
                int z=b-'A';
                e[cnt].u=x; e[cnt].v=z;
                e[cnt].len=l;
                cnt++;
            }
//            scanf("\n");
        }
        res=0;
//        cout<<"11111111"<<endl;
        sort(e,e+cnt);
        Kruskal();
//        cout<<"222"<<endl;
        printf("%d\n",res);
    }
    return 0;
}


ZOJ 1372 

没什么说的  主要是判重的问题  

我是用三维map存储状态  直接判断就可以了。。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <map>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100

#define FOR(i,a,b)  for(int i=a;i<=b;i++)
#define FOV(i,a,b)  for(int i=a;i>=b;i--)
#define REP(i,a,b)  for(int i=a;i<b;i++)
#define REV(i,a,b)  for(int i=a-1;i>=b;i--)
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;
map<int,map<int,int> > mp;
int n,m;
int res;
int parent[60];
struct edge
{
    int u,v;
    int len;
    bool operator<(const edge p)const
    {
        return len<p.len;
    }
};
edge e[3000];

void init()
{
    for(int i=0;i<=n;i++)
        parent[i]=-1;
}

int Find(int x)
{
    int p;
    for(p=x;parent[p]>=0;p=parent[p]);
    while(p!=x)
    {
        int tmp=parent[x];
        parent[x]=p;
        x=tmp;
    }
    return p;
}

void Union(int R1,int R2)
{
    int r1=Find(R1),r2=Find(R2);
    int tmp=parent[r1]+parent[r2];
    if(parent[r1]>parent[r2])
    {
        parent[r1]=r2;
        parent[r2]=tmp;
    }
    else
    {
        parent[r2]=r1;
        parent[r1]=tmp;
    }
}

void Kruskal()
{
    init();
    int num=0;
    for(int i=0;i<m;i++)
    {
        int x,y;
        x=e[i].u; y=e[i].v;
        if(Find(x)!=Find(y))
        {
            res+=e[i].len;
            num++;
            Union(x,y);
        }
        if(num>=n-1)  break;
    }
}

int main()
{
//freopen("ceshi.txt","r",stdin);
//int cs=1;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        scanf("%d",&m);
        for(int i=1;i<=50;i++)
            for(int j=1;j<=50;j++)
                mp[i][j]=0;
        for(int i=0;i<m;i++)
        {
            int x,y,l;
            scanf("%d%d%d",&x,&y,&l);
            if(!mp[x][y])
            {
                e[i].u=x; e[i].v=y;
                e[i].len=l;
                mp[x][y]=1;
            }
            else
            {
                e[i].len=min(l,e[i].len);
            }
        }
        sort(e,e+m);
        res=0;
        Kruskal();
        printf("%d\n",res);
//        cout<<"case "<<cs++<<endl;
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值