2018暑假集训楼下第九场(并查集,prim,kruskal)

The Suspects(POJ 1611)
题意:
SARS 是一种传染性疾病,在某大学爆发了这种既疾病。为了减少对他人的传播,最好的方式是将病人与其他人隔离开。
现在一名学生可能加入多个团体。一个团体的一名学生疑似患病,其他学生也要被隔离。编写一个找到所有患病嫌疑人的程序。
输入:输入文件包含几种情况。每个测试用例以一行中的两个整数n和m开始,其中n是学生数,m是团体的数量。0 在所有情况下,学生0都被识别为患病嫌疑人。该行后面是组的m个成员列表,每组一行。每行以整数k开头,表示组中的成员数。在成员数量之后,有k个整数代表该组中的学生。一行中的所有整数由至少一个空格分隔。n = 0且m = 0的情况表示输入结束,无需处理。
题解:要查询在同一个集合的`成员的数量,并查集模板题。

#include 
#include 
#include 
#include 
using namespace std;
const int N=3e4+7;
int pre[N];
int find_set(int x)
{
    if (x!=pre[x])
        return pre[x]=find_set(pre[x]);
    return x;
}
void merge_set(int x,int y)
{
    int fx=find_set(x);
    int fy=find_set(y);
    if (fx!=fy)
        pre[fx]=fy;
}
int main()
{
    int n,m,num;
    while (~scanf("%d %d",&n,&m)&&(n||m))
    {
        for (int i=0; iwhile (m--)
        {
            scanf("%d",&num);
            int a,b;
            scanf("%d",&a);
            //找哪些学生在同一个集合内
            for (int i=1; iscanf("%d",&b);
                merge_set(a,b);
            }
        }
        //找和0在同一个集合内的学生并计数
        int ans=0;
        for (int i=0; iif (find_set(i)==pre[0])
                ans++;
        printf("%d\n",ans);
    }
    return 0;
}

Agri-Net(POJ 1258)
题意:
John是市长,他当选市长的承诺是为城镇提供互联网连接。John为他的农场订购了网线,并将与其他农民分享他的连接。为了最大限度地降低成本,他希望将最少量的光纤用于将农场连接到所有其他农场。
给出连接每对农场需要多少光纤的列表,您必须找到将它们连接在一起所需的最少光纤数量。每个农场必须与其他农场连接。任何两个农场之间的距离不会超过1e5。
输入:包括几种情况。对于每种情况,第一行包含农场数N(3 <= N <= 100)。以下行包含N x N的矩阵,表示两个农行之间的距离。数字之间用空格隔开。
输出:对于每种情况,输出一个整数长度,它是连接整个服务器集所需的最小光纤长度之和。
题解: 根据题意要找图的最小生成树,prim和kruskal都可以写。

//prim算法实现
#include 
#include 
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e2+7;
int mapp[N][N];
int prim(int s,int n)
{
    int dis[N],v[N],now,m,sum=0;
    for (int i=0; imemset(v,0,sizeof(v));
    v[s]=1,dis[s]=0;
    for (int i=1; i//遍历n-1 次
    {
        m=now=inf;
        for (int j=0; jif (!v[j]&&m>dis[j])
            {
                m=dis[j];
                now=j;
            }
        }
        //提前退出,不是连通图
        if (now==inf)
            return -1;
        v[now]=1,sum+=m;
        //更新已加入生成树点与未加入生成树点之间的最小权值
        for (int j=0; jif (!v[j]&&dis[j]>mapp[now][j])
                dis[j]=mapp[now][j];
    }
    return sum;
}
int main()
{
    int n;
    while (~scanf("%d",&n))
    {
        for (int i=0; ifor (int j=0; jscanf("%d",&mapp[i][j]);
        int sum=prim(0,n);
        if (sum==-1)
            printf("-1\n");
        else
            printf("%d\n",sum);
    }
    return 0;
}
//kruskal算法实现
#include 
#include 
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int N=1e4+7;
int pre[N];
int sum;
struct town
{
    int x,y,power;
} edge[N];
int cmp(town a,town b)
{
    return a.powerint find_set (int x)
{
    int r=x;
    while (pre[r]!=r)
        r=pre[r];
    int i=x;
    int j;
    while (i!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
void merge_set(town a)
{
    int x=find_set(a.x);
    int y=find_set(a.y);
    if (x!=y)
    {
        pre[x]=y;
        sum+=a.power;
    }
}
int main()
{
    int n;
    while (~scanf("%d",&n))
    {
        for (int i=0; i<=n; i++)
            pre[i]=i;
        sum=0;
        int cnt=0;//记录边的条数
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
            {
                edge[cnt].x=i;
                edge[cnt].y=j;
                scanf("%d",&edge[cnt].power);
                if (edge[cnt].power==0)
                    edge[cnt].power=MAX;
                cnt++;
            }
        sort (edge+1,edge+cnt+1,cmp);
        for (int i=1; i<=cnt; i++)
        {
            merge_set(edge[i]);
        }
        printf("%d\n",sum);
    }
    return 0;
}

Bad Cowtractors(POJ 2377)
题意:
Bessie受雇在Farmer John的N(2 <= N <= 1,000)个谷仓中建立廉价的互联网网络。这些谷仓的编号为1..N。 FJ已经进行了一些测量,发现M(1 <= M <= 20,000)条可能在成对的谷仓之间建立路线。每个可能的连接路由具有相关的成本C(1 <= C <= 100,000)。农夫约翰想花最少的钱来连接网络,他甚至不愿意付贝茜。
意识到农夫约翰不会付钱给她,贝茜决定做最糟糕的工作。她必须决定,以便(1)这些连接的总成本尽可能大,(2)所有的谷仓连接在一起(这样就使任意谷仓可以到达其他谷仓的),以及(3)使连接之间没有循环(Farmer John很容易检测到)。
题解:最大生成树,把prim和kruskal的代码稍微一改就可以了。这道题只用kruskal来着,用prim算法实现的代码自己写吧。记录n个谷仓之间需要建n-1条路才能使任意两个谷仓之间都可达,记录建了几条路,可以确定n个谷仓之间是否有最大生成树。

//kruskal算法实现
#include 
#include 
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int sum,coun;
struct town
{
    int x,y,power;
} edge[MAXE];
int cmp(town a,town b)
{
    return a.power>b.power;
}
int find_set (int x)
{
    int r=x;
    while (pre[r]!=r)
        r=pre[r];
    int i=x;
    int j;
    while (i!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}
void merge_set(town a)
{
    int x=find_set(a.x);
    int y=find_set(a.y);
    if (x!=y)
    {
        pre[x]=y;
        sum+=a.power;
        coun++;//记录建路条数
    }
}
int main()
{
    int n,m;
    while (~scanf("%d %d",&n,&m))
    {
        for (int i=0; i<=n; i++)
            pre[i]=i;
        for (int i=0; iscanf("%d %d %d",&edge[i].x,&edge[i].y,&edge[i].power);
        }
        sort (edge,edge+m,cmp);
        sum=coun=0;
        for (int i=0; iif (coun!=n-1)
            printf("-1\n");
        else
            printf("%d\n",sum);
    }
    return 0;
}

畅通工程1(HDU 1232)
题意:
中文,题意不解释。
题解: 并查集经典题。求给出的图有几个集合组成。两个集合需要一条路联通成一个集合,三个集合需要两条路连接成一个集合,n个集合需要n-1条路连接成一个集合。当pre[ i ]== i;时为一个集合。

#include 
#include 
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int find_set (int x)
{
    if (pre[x]!=x)
        return pre[x]=find_set(pre[x]);
    return x;
}
void merge_set(int x,int y)
{
    int fx=find_set(x);
    int fy=find_set(y);
    if (fx!=fy)
    {
        pre[fx]=fy;
    }

}
int main()
{
    int n,m;
    while (~scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        for (int i=0; i<=n; i++)
            pre[i]=i;
        int u,v;
        for (int i=0; iscanf("%d %d",&u,&v);
            merge_set(u,v);
        }

        int coun=0;
        for (int i=1; i<=n; i++)
        {
            if (pre[i]==i)
                coun++;
        }
        printf("%d\n",coun-1);
    }
    return 0;
}

畅通工程2(HDU 1863)
题意:
中文题,不解释。
题解: 裸的求最小生成树。两种算法都可以用,随便挑一种用就可以。

//prim算法实现
#include 
#include 
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e2+7;
int mapp[N][N];
int prim(int s,int n)
{
    int dis[N],v[N],now,m,sum=0;
    for (int i=1; i<=n; i++)
        dis[i]=mapp[s][i];
    memset(v,0,sizeof(v));
    v[s]=1,dis[s]=0;
    for (int i=1; i//遍历n-1 次
    {
        m=now=inf;
        for (int j=1; j<=n; j++)
        {
            if (!v[j]&&m>dis[j])
            {
                m=dis[j];
                now=j;
            }
        }
        //提前退出,不是连通图
        if (now==inf)
            return -1;
        v[now]=1,sum+=m;
        //更新已加入生成树点与未加入生成树点之间的最小权值
        for (int j=1; j<=n; j++)
            if (!v[j]&&dis[j]>mapp[now][j])
                dis[j]=mapp[now][j];
    }
    return sum;
}
int main()
{
    int n,m;
    while (~scanf("%d %d",&n,&m)&&n)
    {
        int u,v,w;
        memset(mapp,inf,sizeof(mapp));
        for (int i=0; iscanf("%d %d %d",&u,&v,&w);
            if (mapp[u][v]>w)
                mapp[u][v]=mapp[v][u]=w;
        }
        int sum=prim(1,m);
        if (sum==-1)
            printf("?\n");
        else
            printf("%d\n",sum);
    }
    return 0;
}

How Many Tables(HDU 1213)
题意: 今天是Ignatius的生日。他邀请了很多朋友。现在是晚餐时间。伊格纳修斯想知道他至少需要多少桌子。你必须注意到并非所有的朋友都互相认识,而且所有的朋友都不想和陌生人呆在一起。
题解: 并查集,查找有多少个集合。

#include 
#include 
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int find_set (int x)
{
    if (pre[x]!=x)
        return pre[x]=find_set(pre[x]);
    return x;
}
void merge_set(int x,int y)
{
    int fx=find_set(x);
    int fy=find_set(y);
    if (fx!=fy)
    {
        pre[fx]=fy;
    }

}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d %d",&n,&m);
        for (int i=0; i<=n; i++)
            pre[i]=i;
        int u,v;
        for (int i=0; iscanf("%d %d",&u,&v);
            merge_set(u,v);
        }

        int coun=0;
        for (int i=1; i<=n; i++)
        {
            if (pre[i]==i)
                coun++;
        }
        printf("%d\n",coun);
    }
    return 0;
}

你可能感兴趣的:(并查集,kruskal,prim,并查集,prim,kruskal)