并查集/最小生成树

我的心愿是世界和平!

并查集

O代表修好该台计算机,S代表询问接下来的两台计算机是否可以正常通信。若A和B可以通信,B和C可以通信,则认为A和B也可以通信。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int s[1010];
int x[1010];
int y[1010];
int found(int x)//查找祖先.
{
    if(s[x]!=x)
        s[x]=found(s[x]);//路径压缩.
    return s[x];
}
void change(int x,int y)
{
    x=found(x);
    y=found(y);
    s[x]=y;
}
int main()
{
    char c;
    int m,n,i,j,p;
    double q,d;
    cin>>m>>d;//共m台计算机,距离小于d时可以通信.
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        s[i]=0;
    }
    getchar();
    while(cin>>c)
    {
        if(c=='O')
        {
            cin>>p;
            if(s[p]==0)
            s[p]=p;
            for(i=1;i<=m;i++)
            {
                if(s[i]!=0)
                {
                    q=sqrt((y[i]-y[p])*(y[i]-y[p])+(x[i]-x[p])*(x[i]-x[p]));
                    if(q<=d)
                        change(p,i);
                }
            }
        }
        if(c=='S')
        {
            cin>>p>>j;
            p=found(p);
            j=found(j);
            if(p==j)
                cout<<"SUCCESS"<<endl;
            else
                cout<<"FAIL"<<endl;
        }
    }
    return 0;
}

题目来源:http://acm.sdibt.edu.cn:8080/vjudge/contest/view.action?cid=1801#problem/E

认为0学生为嫌疑人,所有与其同组的人均认为是嫌疑人。求0所在的集合中的总数。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int s[30010];
int rank[30010];//记录以i为祖先的集合共有多少孩子.
int found(int x)
{
    if(s[x]!=x)
        s[x]=found(s[x]);
    return s[x];
}
void change(int x,int y)
{
    x=found(x);
    y=found(y);
    s[x]=y;
    if(x==y);
    else
    rank[y]+=rank[x];
}
int main()
{
    int m,n,i,j,p,q,o,t;
    while(cin>>m>>n)
    {
        if(m==0&&n==0)
            break;
        for(i=0;i<m;i++)
        {
            rank[i]=1;
            s[i]=i;
        }
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&o,&p);
            for(j=1;j<o;j++)
            {
                scanf("%d",&q);
                change(p,q);
            }
        }
        t=0;
        while(s[t]!=t)
            t=s[t];
        cout<<rank[t]<<endl;
    }
    return 0;
}

题目来源:http://acm.sdibt.edu.cn:8080/vjudge/contest/view.action?cid=1801#problem/F

Prim算法

矩阵模板

#include<stdio.h>
#include<iostream>
using namespace std;
#define max 0x3f3f3f3f;
int map[110][110];//构成一个矩阵,代表村庄间的距离.
int sign[110];//用于标记该村庄是否已经被连接.
int dis[110];//更新到该村庄最小值.
int n,i,j;
void prim()
{
    int sum=0,i,j,num,min;
    for(i=1;i<=n;i++)
    {
        map[i][i]=max;//村庄自己不算做最短路径.
        sign[i]=0;
        dis[i]=map[1][i];//以1村庄为起始点到各村庄间的距离.
    }
    sign[1]=1;//以1村庄为起始点,该村庄被标记.
    for(i=1;i<n;i++)
    {
        num=max;
        min=max;
        for(j=1;j<=n;j++)
        {
            if(sign[j]==0&&dis[j]<=min)//该村庄未被连接且到它的距离最小.
            {
                num=j;//记录下一个被标记的村庄.
                min=dis[j];
            }
        }
        sign[num]=1;
        sum+=min;
        for(j=1;j<=n;j++)
        {
            if(sign[j]==0&&dis[j]>map[num][j])//新被标记的村庄到未被标记的村庄有更短路径,更新dis[]数组的值.
                dis[j]=map[num][j];
        }
    }
    printf("%d\n",sum);
}
int main()
{
    while(cin>>n)//共有n个村庄.
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                scanf("%d",&map[i][j]);//输入村庄之间的距离.
        prim();//利用prim()函数求村庄之间的最小生成树.
    }
}

Kruskal算法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int i,n,m,sum;
struct node{
    int left;
    int right;
    int distance;
}edge[5050];//用于记录从left到right的距离.
int s[5050];//并查集将其联系起来.
bool cmp(struct node a,struct node b)
{
    return a.distance<b.distance;
}//对所有的距离从小到大排序.
int found(int x)
{
    if(s[x]!=x)
        s[x]=found(s[x]);
    return s[x];
}
void change(int x,int y,int n)
{
    int fx=found(x);
    int fy=found(y);
    if(fx!=fy)//祖先相同证明已连接,不同对其进行连接.
    {
        s[fx]=fy;
        sum+=edge[n].distance;
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m),n)
    {
        sum=0;
        for(i=1;i<=m;i++)
            scanf("%d%d%d",&edge[i].left,&edge[i].right,&edge[i].distance);
        for(i=1;i<=m;i++)
            s[i]=i;
        sort(edge+1,edge+m+1,cmp);//排序
        for(i=1;i<=m;i++)
            change(edge[i].left,edge[i].right,i);
        printf("%d\n",sum);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值