UVA1511 Buy or Build 二进制枚举+最小生成树kruskal

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

里面的图片整不下来

题意:T组例子,n个点,qq个套餐,每个点之间都是连通的,权值为两点距离的平方(意思就是可以用int了,但是long long),qq个套餐使其中一些点是连通的,花费为w,然后求一颗最小生成树。

因为不知道哪些套餐搭配能够使费用最低,但是qq最大也只有8,所以可以采用二进制枚举的方式来排出每一种组合,然后根据这些组合跑kruskal,找出其中的最小值。需要注意套餐与套餐之间如果有相同的点是可以把两个套餐的点都连通的。然后各种小细节改了我两个多小时。

CODE

#include"stdio.h"
#include"iostream"
#include"algorithm"
#include"string.h"
#include"math.h"
#include"stdlib.h"
#include"vector"
#include"queue"
#define maxn 1005
#define inf 777777777
typedef long long LL;
using namespace std;

struct node
{
    LL st;
    LL en;
    LL len;
}edge[maxn*maxn];  ///
struct Node
{
    LL x;
    LL y;
}po[maxn];   ///
vector<LL> q[10];  ///套餐
LL n,qq,num;   ///n个点,qq个套餐,num条边
LL fa[maxn];   ///父亲数组
LL w[maxn];   ///每个套餐的花费

bool cmp(node a,node b)   ///由小到大排序
{
    if(a.len < b.len)
        return true;
    return false;
}

LL len(Node a,Node b)   ///每个点与其他点连接的边的权值
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

LL Find(LL x)   ///寻找根节点
{
    if(fa[x] == x)
        return x;
    else
        return fa[x] = Find(fa[x]);
}

LL kruskal()   ///模版
{
    LL ans = 0;
    for(LL i = 0;i < num;i++)
    {
        LL fx = Find(edge[i].st);
        LL fy = Find(edge[i].en);
        if(fx != fy)
        {
            ans += edge[i].len;
            fa[fx] = fy;
        }
    }
    return ans;
}

int main(void)
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&qq);
        for(LL i = 0;i <= qq;i++) q[i].clear();  ///使用之前清空
        for(LL i = 0;i < qq;i++)
        {
            int t;
            scanf("%lld%lld",&t,&w[i]);
            while(t--)
            {
                LL xx;
                scanf("%lld",&xx);
                q[i].push_back(xx);
            }
        }
        for(LL i = 1;i <= n;i++)
        {
            scanf("%lld%lld",&po[i].x,&po[i].y);
        }
        num = 0;   ///边的数量
        for(LL i = 1;i <= n;i++)
        {
            for(LL j = i+1;j <= n;j++)
            {
                edge[num].st = i;
                edge[num].en = j;
                edge[num++].len = len(po[i],po[j]);
            }
        }
        sort(edge,edge+num,cmp);   ///跑kruskal之前必须排序
        for(LL i = 1;i <= n;i++) fa[i] = i;   ///把每个结点的根节点设为本身
        LL ans = kruskal();   ///不加入任何套餐情况下的最小生成树
       // cout<<ans<<endl;
        for(LL i = 1;i <(1<<qq);i++)   ///二进制枚举到每一种情况
        {
            LL temp = 0;   ///使用套餐所使用的花费
            for(LL j = 0;j <= n;j++) fa[j] = j;   ///每次查询都要重新赋值根节点为本身
            for(LL j = 0;j < qq;j++)   ///列举二进制中的每一位
            {
                if(!(i&(1<<j))) continue;  ///如果这一位上为0就跳出
                temp += w[j];
                LL fx = Find(q[j][0]);   ///找到第一个点的根节点
                for(LL k = 1;k < q[j].size();k++)   ///连通整个套餐为同一个根节点,如果如果两个套餐有交织则全部赋为同一个根节点
                {
                    LL fy = Find(q[j][k]);   ///和kruskal里面差不多写法,因为涉及到其他套餐,所以必须这样写
                    if(fx != fy)
                        fa[fy] = fx;
                }
            }
            ans = min(ans,temp+kruskal());  ///找出最小
        }
        printf("%lld\n",ans);   ///%lld!!
        if(T) printf("\n");
    }
    //printf("\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值