uva 1151 Buy or Build

来源:互联网 发布:淘宝夜店装 编辑:程序博客网 时间:2024/05/18 06:19

原题:
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 w c 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 × 10 6 ). 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.

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

中文:

给你二维平面上面一堆点,点时间的距离就是欧几里得距离的平方。现在让你实现一个最小生成树把这些点连起来费用最小,费用值为两点距离。不过,在连接这些点之前,可以花钱买一些已经连接好的网络。现在问你在结合买和建造同时,如何使得最小生成树的值最少。

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=1010;struct point{    int x,y;};struct buy{    int Cost;    vector<int> road;};struct edge{    edge(int xx,int yy,int dd)    {        x=xx;        y=yy;        d=dd;    }    int x,y;    int d;};int n,q,t;int Father[maxn];int Rank[maxn];buy bs[10];point ps[maxn];vector<edge> e,use;int Distance(const point &p1,const point &p2){    return ((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));}void ini(){    for(int i=0;i<=n;i++)    {        Father[i]=i;        Rank[i]=0;    }}int Find(int x){    if(Father[x]==x)        return x;    return Father[x]=Find(Father[x]);}int cmp(const edge &e1,const edge &e2){    return e1.d<e2.d;}bool Merge(int x,int y){    int u=Find(x);    int v=Find(y);    if(u!=v)    {        if(Rank[u]<Rank[v])            Father[u]=v;        else        {            Father[v]=u;            if(Rank[u]==Rank[v])                Rank[u]++;        }        return true;    }    return false;}int kruskal(int cnt,vector<edge> &used){    if(cnt==1)        return 0;    int ans=0;    used.clear();    for(int i=0;i<e.size();i++)    {        if(Merge(e[i].x,e[i].y))        {            ans+=e[i].d;            used.push_back(e[i]);            if(--cnt==1)                break;        }    }    return ans;}int main(){    ios::sync_with_stdio(false);    cin>>t;    int b,c,x;    while(t--)    {        cin>>n>>q;        ini();        e.clear();        use.clear();        for(int i=0;i<q;i++)            bs[i].road.clear();        for(int i=0;i<q;i++)        {            cin>>b>>c;            bs[i].Cost=c;            for(int j=0;j<b;j++)            {                cin>>x;                bs[i].road.push_back(x-1);            }        }        for(int i=0;i<n;i++)            cin>>ps[i].x>>ps[i].y;        for(int i=0;i<n;i++)        {            for(int j=i+1;j<n;j++)            {                double d=Distance(ps[i],ps[j]);                e.push_back(edge(i,j,d));            }        }        ini();        sort(e.begin(),e.end(),cmp);        int ans=kruskal(n,use);        for(int mask=0;mask<(1<<q);mask++)        {            ini();            int cnt=n,c=0;            for(int i=0;i<q;i++)            {                if(mask & (1<<i))                {                    c+=bs[i].Cost;                    for(int j=1;j<bs[i].road.size();j++)                    {                        if(Merge(bs[i].road[j],bs[i].road.front()))                            --cnt;                    }                }            }            vector<edge> dummy;            ans=min(ans,c+kruskal(cnt,dummy));        }        cout<<ans<<endl;        if(t)            cout<<endl;    }    return 0;}

解答:
紫书是上的例题,暴力枚举套餐加上最小生成树就能过。

但是此题书上给了一个优化方法,首先不使用任何套餐跑一次最小生成树,把在这个生成树中的边都保留下来。然后使用位运算的方式枚举套餐,使用套餐中的边和之前使用最小生成树的边再进行最小生成树算法。
不断枚举套餐,更新最小值即可。

为什么这么做可以?

书上的话的意思是,利用kruskal算法的原理,如果一个新的边加入最小生成树,那个这个边一定是经过并查集来判断这个边的两个端点是否属于同一个联通分量,属于同一个联通分量则不会进入最小生成树。那么每次购买套餐以后,相当于把套餐中的边上的权值都变成了0,此时再去利用kruskal算法去找最小的边,这些权值为0的边肯定会排在最前面,而且最先被加入到最小生成树中。其次,如何保证利用套餐中给定的边,再加上单纯使用最小生成树的边就能找一个满足使用套餐的最小生成树呢? 原因很简单,因为如果不使用套餐,计算出来的边就已经是从小到大排序的了,加入几个权值为0的边,结果必定会得到的一个使用套餐的最优结果。所以结论成立。

代码中使用按秩合并,速度没快到哪去 -_-|||

原创粉丝点击