算法竞赛训练指南11.2 最小生成树

Posted jkzr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法竞赛训练指南11.2 最小生成树相关的知识,希望对你有一定的参考价值。

Buy or Build UVA - 1151 

World Wide Networks (WWN) is a leading company that operates large telecommunication networks. WWN would like to setup a new network in Borduria, a nice country that recently managed to get rid of its military dictator Kurvi-Tasch and which is now seeking for investments of international companies (for a complete description of Borduria, have a look to the following Tintin albums “King Ottokar’s Sceptre”, “The Calculus Affair” and “Tintin and the Picaros”). You are requested to help WWN todecide how to setup its network for a minimal total cost.

There are several local companies running small networks (called subnetworks in the following) that partially cover the n largest cities of Borduria. WWN would like to setup a network that connects all n cities. To achieve this, it can either build edges between cities from scratch or it can buy one or several subnetworks from local companies. You are requested to help WWN to decide how to setup its network for a minimal total cost.

? All n cities are located by their two-dimensional Cartesian coordinates.

? There are q existing subnetworks. If q ≥ 1 then each subnetwork c (1 ≤ c ≤ q) is defined by a set of interconnected cities (the exact shape of a subnetwork is not relevant to our problem).

? A subnetwork c can be bought for a total cost wc and it cannot be split (i.e., the network cannot be fractioned).

? To connect two cities that are not connected through the subnetworks bought, WWN has to build an edge whose cost is exactly the square of the Euclidean distance between the cities.

You have to decide which existing networks you buy and which edges you setup so that the total cost is minimal. Note that the number of existing networks is always very small (typically smaller than 8).

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. Each test case is described by one input file that contains all the relevant data. The first line contains the number n of cities in the country (1 ≤ n ≤ 1000) followed by the number q of existing subnetworks (0 ≤ q ≤ 8). Cities are identified by a unique integer value ranging from 1 to n. The first line is followed by q lines (one per subnetwork), all of them following the same pattern: The first integer is the number of cities in the subnetwork. The second integer is the the cost of the subnetwork (not greater than 2 × 106 ). The remaining integers on the line (as many as the number of cities in the subnetwork) are the identifiers of the cities in the subnetwork. The last part of the file contains n lines that provide the coordinates of the cities (city 1 on the first line, city 2 on the second one, etc). Each line is made of 2 integer values (ranging from 0 to 3000) corresponding to the integer coordinates of the city.

Output

For each test case, your program has to write the optimal total cost to interconnect all cities. The outputs of two consecutive cases will be separated by a blank line. A 115 Cities Instance Consider a 115 cities instance of the problem with 4 subnetworks (the 4 first graphs in Figure 1). As mentioned earlier the exact shape of a subnetwork is not relevant still, to keep figures easy to read, we have assumed an arbitrary tree like structure for each subnetworks. The bottom network in Figure 1 corresponds to the solution in which the first and the third networks have been bought. Thin edges correspond to edges build from scratch while thick edges are those from one of the initial networks. Figure 1: A 115 Cities Instance and a Solution (Buying the First and the Third Network)

Sample Explanation: The sample input instance is shown in Figure 2. An optimal solution is described in Figure 3 (thick edges come from an existing network while thin edges have been setup from scratch). Figure 2: The 7 City instance of the sample input Figure 3 An optimal solution of the 7 City instance in which which the first and second existing networkshave been bought while two extra edges (1,5) and (2,4) have been setup

Sample Input 1 7 3 2 4 1 2 3 3 3 6 7 3 9 2 4 5 0 2 4 0 2 0 4 2 1 3 0 5 4 4

Sample Output 17

题意:给你n个点,你的任务是让n个点连通,你可以新建一些边,费用是两个端点的欧几里得距离,另外呢,还有q个套餐给你选择,如果你买了第i个套餐,那么这个套餐内的所有的结点都会变得互相连通,第i个套餐的花费为Ci。

那么第一次想的就是先枚举购买哪些套餐,然后再求最小生成树,结果Tle了。后来看了题解,发现由于枚举量为O(2^q),给边排序为O(n^2logn)的复杂度,再求Kruskal为O(n^2)肯定就炸掉了。

那么正解是你先求出来一次原图的最小生成树,然后枚举套餐后只考虑套餐中的边和这n-1条百年,枚举完套餐再求生成树,边数就会少很多,就没有那么高的复杂度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=1005;
const int INF=0x3f3f3f3f;
int n,m,cnt;
int fa[maxn];
vector<int>G[10];
int cost[maxn];
struct Point
{
    int x,y;
}point[maxn];
struct Node
{
    int from,to;
    int value;
}node[maxn*maxn];
bool cmp(Node a,Node b)
{
    return a.value<b.value;
}
void init()
{
    for(int i=0;i<maxn;i++)
        fa[i]=i;
}
int findd(int x)
{
    if(fa[x]==x)
        return x;
    else
        return fa[x]=findd(fa[x]);
}
int getdist(Point a,Point b)//求边权
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int Kruskal()
{
    int ans=0;
    for(int i=1;i<=cnt;i++)
    {
        int fx=findd(node[i].from);
        int fy=findd(node[i].to);
        if(fx!=fy)
        {
            ans+=node[i].value;
            fa[fx]=fy;
        }
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int num,temp;
        scanf("%d %d",&n,&m);
        for(int i=0;i<=m;i++)
            G[i].clear();
        for(int i=0;i<m;i++)
        {
            scanf("%d %d",&num,&cost[i]);
            for(int j=1;j<=num;j++)
            {
                scanf("%d",&temp);
                G[i].push_back(temp);//记录这个套餐里包含了哪些点
            }
        }
        for(int i=1;i<=n;i++)
            scanf("%d %d",&point[i].x,&point[i].y);
        cnt=0;
        for(int i=1;i<=n;i++)//处理处各个点之间的边权
        {
            for(int j=i+1;j<=n;j++)
            {
                cnt++;
                node[cnt].from=i;node[cnt].to=j;
                node[cnt].value=getdist(point[i],point[j]);
            }
        }
        init();
        sort(node+1,node+cnt+1,cmp);
        int sum=Kruskal();
        //cout<<sum<<endl;
        int c;
        for(int i=0;i<(1<<m);i++)//常用的二进制容斥枚举!!!!
        {
            init();//每次都会进行一次Kruskal所以要重新init()
            c=0;
            for(int j=0;j<m;j++)
            {
                if(i&(1<<j))//得到每次选择了哪些套餐
                {
                    c+=cost[j];//加上套餐费
                    int xx=findd(G[j][0]);
                    for(int k=1;k<G[j].size();k++)//把套餐里面的素有点先处理到一个联通分量重
                    {
                        int xy=findd(G[j][k]);
                        if(xx!=xy)
                            fa[xy]=xx;
                    }
                }
            }
            sum=min(c+Kruskal(),sum);//跑一遍Kruskal算法,看使用这个套餐系列和没有使用这个套餐系列谁耗费低一些。
        }
        printf("%d
",sum);
        if(T)
            printf("
");//注意输出格式
    }
    return 0;
}

Slim Span  UVA - 1395 

题面直接点连接吧,复制上来的乱了。

 

题意:给出一个n个结点的图。求最大边减去最小边的值尽量小的生成树。

首先我们可以知道我们Kruskal选择边权的时候选择到最后的时候都是最大的边,要使得R-L最小,我们可以枚举每个边,先把每个边都放进去,作为最小的边,然后跑Kruskal算法,如果能得到最小生成树我们取最小值。依次跑完所有的边,得到所有的情况的最小生成树,也就得到了最大边减去最小边最小的生成树。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long LL;
const int maxn=10005;
const int INF=0x3f3f3f3f;
int n,m,cnt;
struct Node
{
    int from,to;
    int value;
}node[maxn];
int fa[maxn];
bool cmp(Node a,Node b)
{
    return a.value<b.value;
}
void init()
{
    for(int i=0;i<maxn;i++)
        fa[i]=i;
    cnt=0;
}
int findd(int x)
{
    if(fa[x]==x)
        return x;
    else
        return fa[x]=findd(fa[x]);
}
void join(int x,int y)
{
    int fx=findd(x);
    int fy=findd(y);
    if(fx!=fy)
    {
        cnt++;
        fa[fx]=fy;//注意这里不是x,y
    }
}
int main()
{
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        for(int i=0;i<m;i++)
            scanf("%d %d %d",&node[i].from,&node[i].to,&node[i].value);
        int ans=INF;
        sort(node,node+m,cmp);
        for(int i=0;i<m;i++)//枚举每条边。从小到大的枚举
        {
            init();
            for(int j=i;j<m;j++)//对于每条边跑Kruskal算法
            {
                join(node[j].from,node[j].to);
                if(cnt==n-1)//如果包含这个边生成最小生成树,那么更新值。
                    ans=min(ans,node[j].value-node[i].value);
            }
        }
        if(ans==INF)
            printf("-1
");
        else
            printf("%d
",ans);
    }
    return 0;
}

 

以上是关于算法竞赛训练指南11.2 最小生成树的主要内容,如果未能解决你的问题,请参考以下文章

次小生成树

算法竞赛进阶指南 走廊泼水节

训练指南 UVA - 11354(最小生成树 + 倍增LCA)

挑战程序设计竞赛(算法和数据结构)——15.5最小生成树(Kruskal算法)的JAVA实现

挑战程序设计竞赛(算法和数据结构)——13.2最小生成树(普里姆)的JAVA实现

算法竞赛入门经典——训练指南