其他分享
首页 > 其他分享> > 最小生成树

最小生成树

作者:互联网

一、Kruskal
该算法只与边有关系,适合于稀疏图
大致分为三步:
1.建图
2.sort
3.并查集

struct node
{
	int from,to,dis;//起点,终点,距离
}

bool cmp(node x,node y)
{
	return x.dis < y.dis;
}

int fnd(int x)//递归压缩路径版本
{
	if(pre[x] != x)
	{
		pre[x] = fnd(pre[x]);
	}
	return pre[x];
}

int fnd(int x)//非递归压缩路径版本
{
	int r = x;
	if(pre[r] != r)
	{
		r = pre[r];
	}
	int i = x,j;
	while(i != r)
	{
		 j = pre[i]; // 在改变上级之前用临时变量  j 记录下他的值 
         pre[ i ] = r ; //把上级改为根节点
         i = j;
	}
	return r;
}

void join(int x,int y)
{
	pre[fnd(x)] = fnd(y);
}

for(i=1; i<=n; i++)//初始化
{
	pre[i] = i;
}
sort(a+1,a+1+m,cmp);
k = 0;
ans = 0;
for(i=1; i<=n; i++)
{
	if(k == n - 1)
		break;
	if(fnd a[i].from != fnd a[i].to)//如果两点之间不联通
	{
		join(i,j);
		ans += a[i].dis;
		k++;
	}
}

e.g.
Description
In an episode of the Dick Van Dyke show, little Richie connects the freckles on his Dad’s back to form a
picture of the Liberty Bell. Alas, one of the freckles turns out to be a scar, so his Ripley’s engagement
falls through.
Consider Dick’s back to be a plane with freckles at various (x, y) locations. Your job is to tell Richie
how to connect the dots so as to minimize the amount of ink used. Richie connects the dots by drawing
straight lines between pairs, possibly lifting the pen between lines. When Richie is done there must be
a sequence of connected lines from any freckle to any other freckle.
Input
The input begins with a single positive integer on a line by itself indicating the number of the cases
following, each of them as described below. This line is followed by a blank line, and there is also a
blank line between two consecutive inputs.
The first line contains 0 < n ≤ 100, the number of freckles on Dick’s back. For each freckle, a line
follows; each following line contains two real numbers indicating the (x, y) coordinates of the freckle.
Output
For each test case, the output must follow the description below. The outputs of two consecutive cases
will be separated by a blank line.
Your program prints a single real number to two decimal places: the minimum total length of ink
lines that can connect all the freckles.
Sample Input
1
3
1.0 1.0
2.0 2.0
2.0 4.0
Sample Output
3.41

给出n个点的横纵坐标 用最短的直线把它们连起来 使得每两个点之间都有线连起来 直接Kruskal就完事了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
double x[105],y[105];
int n,m,kase,pre[105];

struct node
{
    int l,r;
    double dis;
}a[10005];

bool cmp(node x, node y)
{
    return x.dis < y.dis;
}

double len(int i,int j)
{
    return (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
}

int fnd(int x)
{
    while(pre[x] != x)
        x = pre[x];
    return x;
}

void join(int x,int y)
{
    pre[fnd(x)] = fnd(y);
}

int main()
{
    int i,j,k;
    double ans;
    scanf("%d",&kase);
    while(kase--)
    {
        scanf("%d",&n);
        for(i=1; i<=n; i++)
        {
            scanf("%lf%lf",&x[i],&y[i]);
        }
        m = 0;
        for(i=1; i<n; i++)
        {
            for(j=i+1; j<=n; j++)
            {
                m++;
                a[m].l = i;
                a[m].r = j;
                a[m].dis = len(i,j);
            }
        }
        for(i=1; i<=n; i++)
        {
            pre[i] = i;
        }
        sort(a+1,a+1+m,cmp);
        k = 0;
        ans = 0;
        for(i=1; i<=m; i++)
        {
            if(k == n-1)
                break;
            if(father(a[i].l) != father(a[i].r))
            {
                join(a[i].l,a[i].r);
                ans += sqrt(a[i].dis);
                k++;
            }
        }
        printf("%.2f\n",ans);
    }
}

Description
Many new buildings are under construction on the campus of the University of Waterloo. The university
has hired bricklayers, electricians, plumbers, and a computer programmer. A computer programmer?
Yes, you have been hired to ensure that each building is connected to every other building (directly or
indirectly) through the campus network of communication cables.
We will treat each building as a point specified by an x-coordinate and a y-coordinate. Each
communication cable connects exactly two buildings, following a straight line between the buildings.
Information travels along a cable in both directions. Cables can freely cross each other, but they are
only connected together at their endpoints (at buildings).
You have been given a campus map which shows the locations of all buildings and existing communication cables. You must not alter the existing cables. Determine where to install new communication
cables so that all buildings are connected. Of course, the university wants you to minimize the amount
of new cable that you use.
Input
The input file describes several test cases. The description of each test case is given below:
The first line of each test case contains the number of buildings N (1 ≤ N ≤ 750). The buildings
are labeled from 1 to N. The next N lines give the x and y coordinates of the buildings. These
coordinates are integers with absolute values at most 10000. No two buildings occupy the same point.
After that there is a line containing the number of existing cables M (0 ≤ M ≤ 1000) followed by M
lines describing the existing cables. Each cable is represented by two integers: the building numbers
which are directly connected by the cable. There is at most one cable directly connecting each pair of
buildings.
Output
For each set of input, output in a single line the total length of the new cables that you plan to use
rounded to two decimal places.
Sample Input
4
103 104
104 100
104 103
100 100
1
4 2
4
103 104
104 100
104 103
100 100
1
4 2
Sample Output
4.41
4.41

给出n个点的横纵坐标 其中有些点已经连起来了 用最短的直线把所有点连起来 使得两两之间有连线

先把已经连起来的点做个并查集 再kruskal

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,x[1000],y[1000],pre[1000];
struct node
{
    int l,r;
    double dis;
}a[500000];

double len(int i,int j)
{
    return (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
}

bool cmp(node x,node y)
{
    return x.dis < y.dis;
}

int fnd(int x)
{
    if(pre[x] != x)
        pre[x] = fnd(pre[x]);
    return pre[x];
}

void join(int x,int y)
{
    pre[fnd(x)] = fnd(y);
}

int main()
{
    int i,j,p,q,k;
    while(scanf("%d",&n) != EOF)
    {
        for(i=1; i<=n; i++)
        {
            scanf("%d%d",&x[i],&y[i]);
        }
        k = 0;
        for(i=1; i<n; i++)
        {
           for(j=i+1; j<=n; j++)
           {
               k++;
               a[k].l = i;
               a[k].r = j;
               a[k].dis = len(i,j);
           }
        }
        for(i=1; i<=n; i++)
        {
            pre[i] = i;
        }
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d",&p,&q);
            if(fnd(p) != fnd(q))
                join(p,q);
        }
        sort(a+1,a+1+k,cmp);
        double ans = 0;
        for(i=1; i<=k; i++)
        {
            if(fnd(a[i].l) != fnd(a[i].r))
            {
                join(a[i].l,a[i].r);
                ans += sqrt(a[i].dis);
            }
        }
        printf("%.2lf\n",ans);
    }
}

Description
The Department of National Defence (DND) wishes to connect several northern outposts by a wireless network. Two different communication technologies are to be
used in establishing the network:
every outpost will have a radio
transceiver and some outposts will
in addition have a satellite channel.
Any two outposts with a satellite channel can communicate via
the satellite, regardless of their location. Otherwise, two outposts
can communicate by radio only if
the distance between them does not
exceed D, which depends of the
power of the transceivers. Higher
power yields higher D but costs
more. Due to purchasing and
maintenance considerations, the
transceivers at the outposts must
be identical; that is, the value of D
is the same for every pair of outposts.
Your job is to determine the
minimum D required for the
transceivers. There must be at
least one communication path (direct or indirect) between every pair
of outposts.
Input
The first line of input contains N, the number of test cases. The first line of each test case contains
1 ≤ S ≤ 100, the number of satellite channels, and S < P ≤ 500, the number of outposts. P lines
follow, giving the (x, y) coordinates of each outpost in km (coordinates are integers between 0 and
10,000).
Output
For each case, output should consist of a single line giving the minimum D required to connect the
network. Output should be specified to 2 decimal points.
Sample Input
1
2 4
0 100
0 300
0 600
150 750
Sample Output
212.13

给出n个点的横纵坐标 其中有m条边可以看做已经连起来了 现在要你找出除了那m条边之外的最长边 该最长边要尽可能短

根据贪心 该m条边一定是树上最长的m条 我们把树上的边存到一个数组中 由于已排序 所以数组中元素已经由小到大排好序了 所以数组元素个数减去m的元素即为所求边

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,pre[505],x[505],y[505];
struct node
{
    int l,r;
    double dis;
}a[250050];

double len(int i,int j)
{
    return (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
}

bool cmp(node x,node y)
{
    return x.dis < y.dis;
}

int fnd(int x)
{
    if(pre[x] != x)
        pre[x] = fnd(pre[x]);
    return pre[x];
}

void join(int x,int y)
{
    pre[fnd(x)] = fnd(y);
}

int main()
{
    int i,j,kase,k;
    scanf("%d",&kase);
    while(kase--)
    {
        scanf("%d%d",&m,&n);
        for(i=1; i<=n; i++)
        {
            scanf("%d%d",&x[i],&y[i]);
        }
        k = 0;
        for(i=1; i<n; i++)
        {
            for(j=; j<=n; j++)
            {
                k++;
                a[k].l = i;
                a[k].r = j;
                a[k].dis = len(i,j);
            }
        }
        for(i=1; i<=n; i++)
        {
            pre[i] = i;
        }
        sort(a+1,a+1+k,cmp);
        double ans[505];
        int kount = 0;
        for(i=1; i<=k; i++)
        {
            if(fnd(a[i].l) != fnd(a[i].r))
            {
                join(a[i].l,a[i].r);
                ans[kount++] = sqrt(a[i].dis);
            }
        }
        printf("%.2lf\n",ans[kount-m]);
    }
}

二、Prim
从任意一个顶点开始,每次寻找与当前点最近的点,并将两顶点的距离加入树中。
大致分三步:
1.建图
2.选任意一个点作为初始点,标记为已访问,计算所有与之相连的点的距离,并从中选择距离最短的点,标记为已访问,把距离加入树中。
3.重复以下操作:
每找到一个点,就更新剩余点到已访问点的最短距离,再从剩余点中找到未访问点到已访问点距离最短的点,并标记为已访问。

以上面第一题为例

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define INF 0x3f3f3f3f;
int n,m,vis[105];
double x[105],y[105];

double G[105][105],dist[105];//G用来存图,dist代表每个点到已访问点的最短距离

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

int main()
{
   int i,j,k;
   scanf("%d",&k);
   while(k--)
   {
       scanf("%d",&n);
       for(i=1; i<=n; i++)
       {
           scanf("%lf%lf",&x[i],&y[i]);
       }
       for(i=1; i<=n; i++)
       {
           for(j=1; j<=n; j++)
           {
               G[i][j] = G[j][i] = len(i,j);
           }
       }
       memset(vis,0,sizeof(vis));
       memset(dist,0x3f,sizeof(dist));
       for(i=1; i<=n; i++)
       {
           dist[i] = G[1][i];//把1看成源点,更新与源点相连的点的距离
       }
       vis[1] = 1;//把vis[1]标记为已访问
       double minn,sum = 0;
       int index;
       for(i=2; i<=n; i++)//从剩下的点中找和已标记点距离最近的点
       {
           minn = INF;
           for(j=1; j<=n; j++)
           {
               if(!vis[j] && dist[j] < minn)
               {
                   minn = dist[j];
                   index = j;
               }
           }
           vis[index] = j;
           sum += minn;
           for(j=1; j<=n; j++)//更新剩余点到已访问点的最短距离
           {
               if(!vis[j] && dist[j] > G[index][j])
               {
                   dist[j] = G[index][j];
               }
           }
       }
       printf("%.2f\n",sum);
       if(k)
       printf("\n");
   }
}

标签:pre,int,最小,生成,fnd,each,line,include
来源: https://blog.csdn.net/weixin_43678350/article/details/87564452