1. 程式人生 > >算法競賽訓練指南11.2 最小生成樹

算法競賽訓練指南11.2 最小生成樹

font gre 結果 ostream 沒有 instance tween rap code

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\n",sum);
        if(T)
            printf("\n");//註意輸出格式
    }
    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\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

算法競賽訓練指南11.2 最小生成樹