UVA 1151 Buy or Build

题目链接  http://acm.hust.edu.cn/vjudge/problem/36013

题意

平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,
为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方。
另外还有q(0<=q<=8)个套餐,可以任意购买,即可以不买,也可以买1个 2个 等等 
如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci。
求最小花费。

就是求最小生成树
对于套餐可以用子集枚举处理, 集合S表示套餐的购买状态

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#include <stack>
#include <string>
#include <map>
#include <set>
#define pi acos(-1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int, int> P;
const int maxn = 1e6 + 5;

struct Pos
{
	int x, y;
}pos[1005];
struct Way
{
	int u, v, len;
	bool operator < (const Way &a) const
	{return len < a.len;}
}way[maxn];
int ret[1005], fir = 1;
int cost[10];
vector<int>subnet[10];

int cal(int a, int b)
{
	int x = pos[a].x - pos[b].x;
	int y = pos[a].y - pos[b].y;
	return x*x + y*y;
}
int cmp(Way a, Way b)
{
	if (a.len < b.len) return 1;
	return 0;
}
int find(int a)
{
	if (ret[a] == -1) return a;
	return ret[a] = find(ret[a]);
}
int main(void)
{
//	freopen("C:\\Users\\wave\\Desktop\\NULL.exe\\NULL\\in.txt","r", stdin);
	int T, n, q, i, j, k, t, cnt, sum, ans, t_cnt, S;
	cin >> T;
	while (T--)
	{
		if (fir) fir = 0;
		else cout << endl;
		cin >> n >> q;
		for (i = 1; i <= q; i++){
			scanf("%d %d", &t, &cost[i]);
			subnet[i].clear();
			for (j = 1; j <= t; j++){
				scanf("%d", &k);
				subnet[i].push_back(k);
			}
		}
		for (i = 1; i <= n; i++)
			scanf("%d %d", &pos[i].x, &pos[i].y);
		cnt = 0;
		for (i = 1; i <= n; i++){
			for (j = i+1; j <= n; j++){
				way[++cnt].u = i;
				way[cnt].v = j;
				way[cnt].len = cal(i, j);
			}
		}
		sort(way+1, way+1+cnt);
		for (i = 1; i <= cnt; i++)
		memset(ret, -1, sizeof(ret));
		ans = t_cnt = 0;
		for (i = 1; i <= cnt; i++){ //  先处理一遍没买套餐的 
			int x = find(way[i].u);
			int y = find(way[i].v);
			if (x == y) continue;
			ret[x] = y;
			ans += way[i].len;
			t_cnt++;
			if (t_cnt == n-1) break;
		}
		for (S = 1; S < (1<<q); S++){ // 枚举套餐状态 
			memset(ret, -1, sizeof(ret));
			sum = t_cnt = 0;
			for (i = 1; i <= q; i++){
				if (!(S & (1<<(i-1)))) continue;
				t = subnet[i][0];
				sum += cost[i];
				for (j = 1; j < subnet[i].size(); j++){
					int x = find(subnet[i][j]);
					int y = find(t);
					if (x != y){
						ret[x] = y;
						t_cnt++;
					}
				}
			}
			for (i = 1; i <= cnt; i++){
				if (t_cnt == n-1) break;
				int x = find(way[i].u);
				int y = find(way[i].v);
				if (x == y) continue;
				ret[x] = y;
				sum += way[i].len;
				t_cnt++;
			}
			ans = min(ans, sum);
		}
		cout << ans << endl;
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值