Buy or Build UVA - 1151

紫薯上的原题(P358)。

可以通过枚举选择多少条已经建好的边来做,即枚举子集。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define abs(x) (x >= 0 ? x : -(x))

using namespace std;

const int MAXN = 1000 + 3;
const int INF = 0x7fffffff;
typedef struct coordinate{
	int x, y;
	void Update(int a, int b){
		x = a;	y = b;
	}
}Coordinate;
typedef struct edge{
	int u, v, weight;
	void Update(int a, int b, int c){
		u = a;	v = b;	weight = c;
	}
	bool operator < (const edge &rhs) const{
		return weight < rhs.weight;
	}
}Edge;
typedef struct remaining{
	int num;
	int dot[MAXN], cost;
}Remaining;

int n, m, k, ans;
int dad[MAXN];
Coordinate c[MAXN];
Edge e[MAXN * MAXN];
Remaining r[10];

void link(int u, int v){
	dad[v] = u;
}

int find(int i){
	if(dad[i] == i)	return i;
	return dad[i] = find(dad[i]);
}

bool Union(int u, int v){
	if(find(u) == find(v))	return false;
	link(find(u), find(v));	return true;
}

void reset_Union(){
	for(int i = 1; i <= n; i++)	dad[i] = i;
}

int dfs(int subset){
	reset_Union();	int res = 0, cnt = 1;
	for(int i = 0; i < m; i++)
		if(subset & (1 << i)){
			res += r[i].cost;
			for(int j = 1; j < r[i].num; j++)	
				if(Union(r[i].dot[j - 1], r[i].dot[j])){
					if(++cnt == n)	return res;
				}
		}
	for(int i = 0; i < k; i++)
		if(Union(e[i].u, e[i].v)){
			res += e[i].weight;	if(++cnt == n)	return res;
		}
}

void solve()
{
	ans = INF;
	for(int i = 0; i < (1 << m); i++){
		//printf("i = %d, dfs(i) = %d\n", i, dfs(i));
		ans = min(ans, dfs(i));
	}
	printf("%d\n", ans);
}

void init()
{
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i++){
		scanf("%d%d", &r[i].num, &r[i].cost);
		for(int j = 0; j < r[i].num; j++)	scanf("%d", &r[i].dot[j]);
	}
	for(int i = 1; i <= n; i++)	scanf("%d%d", &c[i].x, &c[i].y);
	k = 0;
	for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++)	e[k++].Update(i, j, (c[j].y - c[i].y) * (c[j].y - c[i].y) + (c[j].x - c[i].x) * (c[j].x - c[i].x)); 
	sort(e, e + k);
}

int main(void)
{
	int times;	scanf("%d", ×);
	for(int i = 0; i < times; i++){
		if(i)	printf("\n");	
		init(); solve();
	}	
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值