并查集,最小生成树 prim算法 kruskal算法

简单例题

畅通工程

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

Input

测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最少还需要建设的道路数目。

Sample Input

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

Sample Output

1
0
2
998

Hint

   Huge input, scanf is recommended.
        
 
#include<iostream>
using namespace std;
int fa[1002];
int seek(int x)//路径压缩
{
    return fa[x]==x ? x : seek(fa[x]);
}
void unite(int x,int y)
{
    int fx,fy;
    fx=seek(x);
    fy=seek(y);
    if(fx!=fy)
        fa[fx]=fy;
}
int main()
{
    int n,m;//n个城镇,已经修了m条路
    while(cin>>n>>m,n){
        for(int i=1;i<=n;i++){
            fa[i]=i;//初始化,自己是自己的上级
        }
        int x,y;
        for(int i=1;i<=m;i++){
            cin>>x>>y;
            unite(x,y);
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(fa[i]==i){
                cnt++;
            }
        }
        cout<<cnt-1<<endl;//cnt棵树,至少需要cnt-1个路
    }
    return 0;
}

The Suspects

Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.

Input

The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.

Output

For each case, output the number of suspects in one line.

Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0

Sample Output

4
1
1

题意:编号为0的这个学生是嫌疑人,和ta同一个组的人都是嫌疑人,求总共有多少个嫌疑人.

#include<iostream>
using namespace std;
const int maxn=30000+3;
int fa[maxn];
int seek(int x)
{
    return fa[x]==x ? x : seek(fa[x]);
}
void unite(int x,int y)
{
    int fx,fy;
    fx=seek(x);
    fy=seek(y);
    if(fx==0){//fx或者fy不一定为祖宗,只要等于0就把0设为祖宗
        fa[fy]=0;
        return ;
    }
    if(fy==0){
        fa[fx]=0;
        return ;
    }
    if(fx!=fy)
        fa[fx]=fy;
}
int main()
{
    int n,m;//n个学生,m个小组
    while(cin>>n>>m,n||m){
        for(int i=0;i<n;i++)
            fa[i]=i;
        int a,b,k;
        for(int i=1;i<=m;i++){
            cin>>k;
            if(k>0)
                cin>>a;
            k--;
            while(k>0){
                cin>>b;
                unite(a,b);
                k--;
            }
        }
        int cnt=0;
        for(int i=0;i<n;i++){
            if(seek(i)==0)
                cnt++;
        }
        cout<<cnt<<endl;
    }
    return 0;
}

 下面这个要用prim算法 

/*prim适用于边多点少,复杂度为O(V*V)*/

还是畅通工程

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最小的公路总长度。

Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

Sample Output

3
5


        
  
Huge input, scanf is recommended.

prim算法  求最小生成树

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int INF =0x3f3f3f3f;
const int maxn =110;
int mincost[maxn];//从集合x出发到每个顶点的最短距离
int cost[maxn][maxn];//表示边的权值,不存在设为INF
bool used[maxn];//顶点i是否包含在集合x中
int n;//村庄个数,也就是顶点个数
int prim()
{
    for(int i=1;i<=n;i++)
        mincost[i]=INF;
    mincost[1]=0;
    int ans=0;
    while(true){
//		for(int i = 1 ; i <= n ; i++)
//			if(used[i]||mincost[i]==INF)
//				cout<<"∞  ";
//			else
//				cout<<" "<<mincost[i]<<"  ";
//		cout<<endl;

        int v=-1;//vertex顶点
        //从不属于x的顶点中选取从x到其权值最小的点
        for(int i=1;i<=n;i++)
            if(used[i]==false)
                if(v==-1||mincost[i]<mincost[v])
                    v=i;
        if(v==-1)
            return ans;
        used[v]=true;//把顶点v加入x
        ans+=mincost[v];//把边的长度加到结果里
        for(int i=1;i<=n;i++)
            if(used[i]==0)
                mincost[i]=min(mincost[i],cost[v][i]);
    }
}
int main()
{
    while(cin>>n,n){
        int m=n*(n-1)/2;
        memset(cost,INF,sizeof cost);
        memset(used,0,sizeof used);
        for(int i=0;i<m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            cost[a][b]=c;
            cost[b][a]=c;
        }
        cout<<prim()<<endl;
    }
    return 0;
}

Highways

The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has no public highways. So the traffic is difficult in Flatopia. The Flatopian government is aware of this problem. They're planning to build some highways so that it will be possible to drive between any pair of towns without leaving the highway system.

Flatopian towns are numbered from 1 to N. Each highway connects exactly two towns. All highways follow straight lines. All highways can be used in both directions. Highways can freely cross each other, but a driver can only switch between highways at a town that is located at the end of both highways.

The Flatopian government wants to minimize the length of the longest highway to be built. However, they want to guarantee that every town is highway-reachable from every other town.

Input

The first line of input is an integer T, which tells how many test cases followed.
The first line of each case is an integer N (3 <= N <= 500), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 65536]) between village i and village j. There is an empty line after each test case.

Output

For each test case, you should output a line contains an integer, which is the length of the longest road to be built such that all the villages are connected, and this value is minimum.

Sample Input

1

3
0 990 692
990 0 179
692 179 0

Sample Output

692

Hint

Huge input,scanf is recommended.

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int INF =0x3f3f3f3f;
const int maxn =500+5;
int mincost[maxn];//从集合x出发到每个顶点的最短距离
int cost[maxn][maxn];//表示边的权值,不存在设为INF
bool used[maxn];//顶点i是否包含在集合x中
int n;//村庄个数,也就是顶点个数
int Max;
int prim()
{
    for(int i=1;i<=n;i++)
        mincost[i]=INF;
    mincost[1]=0;
    int ans=0;
    while(true){
        int v=-1;//vertex顶点
        for(int i=1;i<=n;i++)
            if(used[i]==false)
                if(v==-1||mincost[i]<mincost[v])
                    v=i;
        if(v==-1)
            return ans;
        used[v]=1;
            if(mincost[v]>Max)
                ans=mincost[v];
        for(int i=1;i<=n;i++){
            if(used[i]==0)
                mincost[i]=min(mincost[i],cost[v][i]);
            Max=mincost[v];
        }
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        memset(cost,INF,sizeof cost);
        memset(used,0,sizeof used);
        int a,b,c;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
               scanf("%d",&cost[i][j]);
            }
        }
        cout<<prim()<<endl;
    }
    return 0;
}

(让输出?的)畅通工程

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。

Output

对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。

Sample Input

3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100

Sample Output

3
?
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int INF =0x3f3f3f3f;
const int maxn =100+5;
int mincost[maxn];//从集合x出发到每个顶点的最短距离
int cost[maxn][maxn];//表示边的权值,不存在设为INF
bool used[maxn];//顶点i是否包含在集合x中
int m,n;//m为可以修建的道路条数,n为村庄个数
int prim()
{
    for(int i=1;i<=n;i++)
        mincost[i]=INF;
    mincost[1]=0;
    int ans=0;
    while(true){
        int v=-1;//vertex顶点
        for(int i=1;i<=n;i++)
            if(used[i]==false)
                if(v==-1||mincost[i]<mincost[v])
                    v=i;
        if(v==-1)
            return ans;
        used[v]=1;
        ans+=mincost[v];
        for(int i=1;i<=n;i++)
            if(used[i]==0)
                mincost[i]=min(mincost[i],cost[v][i]);
    }
}
int main()
{
    while(cin>>m>>n,m){
        memset(cost,INF,sizeof cost);
        memset(used,0,sizeof used);
        for(int i=1;i<=m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            cost[a][b]=c;
            cost[b][a]=c;
        }
        int res=prim();
        res<INF?cout<<res:cout<<"?";
        cout<<endl;
    }
    return 0;
}

Networking

You are assigned to design network connections between certain points in a wide area. You are given a set of points in the area, and a set of possible routes for the cables that may connect pairs of points. For each possible route between two points, you are given the length of the cable that is needed to connect the points over that route. Note that there may exist many possible routes between two given points. It is assumed that the given possible routes connect (directly or indirectly) each two points in the area. 
Your task is to design the network for the area, so that there is a connection (direct or indirect) between every two points (i.e., all the points are interconnected, but not necessarily by a direct cable), and that the total length of the used cable is minimal.

Input

The input file consists of a number of data sets. Each data set defines one required network. The first line of the set contains two integers: the first defines the number P of the given points, and the second the number R of given routes between the points. The following R lines define the given routes between the points, each giving three integer numbers: the first two numbers identify the points, and the third gives the length of the route. The numbers are separated with white spaces. A data set giving only one number P=0 denotes the end of the input. The data sets are separated with an empty line. 
The maximal number of points is 50. The maximal length of a given route is 100. The number of possible routes is unlimited. The nodes are identified with integers between 1 and P (inclusive). The routes between two points i and j may be given as i j or as j i. 

Output

For each data set, print one number on a separate line that gives the total length of the cable used for the entire designed network.

Sample Input

1 0

2 3
1 2 37
2 1 17
1 2 68

3 7
1 2 19
2 3 11
3 1 7
1 3 5
2 3 89
3 1 91
1 2 32

5 7
1 2 5
2 3 7
2 4 8
4 5 11
3 5 10
1 5 6
4 2 12

0

Sample Output

0
17
16
26

这个题会有重边,是重边的更新最小值

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int INF =0x3f3f3f3f;
const int maxn =50+5;
int mincost[maxn];//从集合x出发到每个顶点的最短距离
int cost[maxn][maxn];//表示边的权值,不存在设为INF
bool used[maxn];//顶点i是否包含在集合x中
int n,m;//n为村庄个数,也就是顶点个数
int prim()
{
    for(int i=1;i<=n;i++)
        mincost[i]=INF;
    mincost[1]=0;
    int ans=0;
    while(true){
        int v=-1;//vertex顶点
        for(int i=1;i<=n;i++)
            if(used[i]==false)
                if(v==-1||mincost[i]<mincost[v])
                    v=i;
        if(v==-1)
            return ans;
        used[v]=1;
        ans+=mincost[v];
        for(int i=1;i<=n;i++)
            if(used[i]==0)
                mincost[i]=min(mincost[i],cost[v][i]);
    }
}
int main()
{
    while(cin>>n,n){
        cin>>m;
        memset(cost,INF,sizeof cost);
        memset(used,0,sizeof used);
        int a,b,c;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            if(cost[a][b]==INF||(cost[a][b]!=INF&&cost[a][b]>c)){//判断是否有重边,有就更新最小值
                cost[a][b]=c;
                cost[b][a]=c;
            }
        }
        cout<<prim()<<endl;
    }
    return 0;
}

Kruskal模板

#include<bits/stdc++.h>
using namespace std;
const int maxn= 1e5;
int fa[maxn];
int n,m;
struct node
{
    int u,v,w;
}st[maxn];
int seek(int x)
{
    return fa[x]==x?x:seek(fa[x]);
}
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){//初始化
        fa[i]=i;
    }
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        st[i].u=a;
        st[i].v=b;
        st[i].w=c;
    }
    sort(st+1,st+m+1,cmp);//注意:从i=1开始输入的,所以sort这里也要+1 !!!!!!!
    long long ans=0;
    for(int i=1;i<=m;i++){
        int x=seek(st[i].u);
        int y=seek(st[i].v);
        if(x!=y){//如果不在一个集合里,也就是不是一个祖宗
            ans+=st[i].w;
            fa[x]=y;
        }
    }
    cout<<ans<<endl;
    return 0;
}

来一道例题:

最小生成树

题目描述
n个点,m条边,每个边会有一个价值。求能连成一棵树的最小价值和?不不不,求这个最小价
值和在n次方情况下对1000000007取模。怎么样,够简单吧!

输入
第一行两个整数n,m(n大于等于1,小于等于1000)(m大于等于0,小于等于100000
接下来m行,每行有两个点i、j(i和j在1到n之间的整数),和一个价值k(k在0和1000之间的整
数)
输出
输出一个对1000000007取模的整数 ,如果连不成一棵树,输出-1

输入样例
4 4
1 2 6
1 3 4
1 4 5
4 2 3
输出样例
20736

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn= 1e5;
int fa[maxn];
int n,m;//n个点,m条边

ll fast_pow(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b){
        if(b&1)
            ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
struct node
{
    int u,v,w;
}st[maxn];
int seek(int x)
{
    return fa[x]==x?x:seek(fa[x]);
}
bool cmp(node a,node b)
{
    return a.w<b.w;
}
int main()
{
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            fa[i]=i;
        }
        for(int i=1;i<=m;i++){
            int a,b,c;
            cin>>a>>b>>c;
            st[i].u=a;
            st[i].v=b;
            st[i].w=c;
        }
        sort(st+1,st+m+1,cmp);//注意:从i=1开始输入的,所以sort这里也要+1 !!!!!!!
        ll ans=0;
        for(int i=1;i<=m;i++){
            int x=seek(st[i].u);
            int y=seek(st[i].v);
            if(x!=y){//如果不在一个集合里,也就是不是一个祖宗
                ans+=st[i].w;
                fa[x]=y;
            }
        }
        int root=0;
        for(int i=1;i<=n;i++){
            if(fa[i]==i){
                root++;
            }
        }
        if(root>1){//如果根多于1个,就说明不止一棵树,也就是连不成一棵树
            cout<<"-1"<<endl;
        }
        else{
            ll res;
            res=fast_pow(ans,n,1000000007);
            cout<<res<<endl;
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/VP_Death_Note/article/details/81705432