这题LRJ书上翻译的有问题,书上说两点之间的cost是两点的欧几里得距离,而题目要求两点的距离是两点欧几里得距离的平方。
其余就没什么好说的了,裸的并查集,需要注意的就是二进制枚举子集的问题。
二进制枚举子集:
for(int i = 0 ; i < (1 << s) ; i++){ /*s是集合元素的个数*/
for(int j = 0 ; j < s ; j++){
if(!(s >> j) & 1)
continue;
else{
}
}
}
14054883 | 1151 | Buy or Build | Accepted | C++ | 0.118 | 2014-08-17 10:37:07 |
注释都加了,就不多解释了:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<list>
#include<cmath>
#include<string>
#include<sstream>
#include<ctime>
using namespace std;
#define _PI acos(-1.0)
#define esp 1e-9
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pill;
/*===========================================
===============KinderRiven===================
===========================================*/
#define MAXD 1100 /*最多1000个点*/
vector<int>array;
int n , m , line_size;
int fa[MAXD];
struct Line{
int l;
int r;
int cost;
friend bool operator < (Line p,Line q){
if(p.cost < q.cost)
return true;
else
return false;
}
}line[MAXD * MAXD];
struct Point{
int x;
int y;
}p[MAXD];
struct Pow{
int cost;
int size;
int arr[MAXD];
}q[15];
int _dist(Point p,Point q){
int x = p.x - q.x;
int y = p.y - q.y;
int ans = x * x + y * y;
return ans;
}
int find_father(int u){
return fa[u] == u ? u : fa[u] = find_father(fa[u]);
}
void init(){
for(int i = 0 ; i <= n ; i++) fa[i] = i;
return ;
}
LL solve(int u){
int now = 0; /*加的边数*/
int cost = 0;
init();
for(int i = 0 ; i < m ; i ++){
if(!((u >> i) & 1)) continue; /*二级制枚举子集*/
cost += q[i].cost; /*套餐的花费*/
for(int j = 1 ; j < q[i].size ; j++){
int t1 = find_father(q[i].arr[j]);
int t2 = find_father(q[i].arr[j - 1]);
if(t1 != t2){
fa[t1] = t2;
now ++;
}
}
}
for(int i = 0 ; now < n - 1; i++){
int c = array[i]; /*找到边的编号*/
int _t1 = line[c].l; /*找到边的两个端点*/
int _t2 = line[c].r;
int t1 = find_father(_t1);
int t2 = find_father(_t2);
if(t1 != t2){
cost += line[c].cost;
fa[t1] = t2;
now++;
}
}
return cost;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
array.clear();
LL _ans = 0;
scanf("%d%d",&n,&m);
init();
for(int i = 0 ; i < m ; i++){
scanf("%d%d",&q[i].size,&q[i].cost);
for(int j = 0 ; j < q[i].size ; j ++)
scanf("%d",&q[i].arr[j]);
}
line_size = 0;
/*求出所有的边*/
for(int i = 1 ; i <= n ; i++){
scanf("%d%d",&p[i].x,&p[i].y); /*输出坐标*/
for(int j = 1 ; j < i ; j++){ /*求出1 ~ i - 1个点与i的距离*/
line[line_size].l = j;
line[line_size].r = i;
line[line_size].cost = _dist(p[i],p[j]); /*求出2个点如果连接需要的花费*/
line_size ++;
}
}
/*找最短路,并记录最短路的每条边*/
/*先排序*/
sort(line,line + line_size);
for(int i = 0 ,j = 0 ; j < n - 1; i ++){ /*当连线为n - 1的时候说明n个点都连通了*/
int l = line[i].l , r = line[i].r; /*找一个线段的2个端点*/
int fa_l = find_father(l); /*利用并查集找出其连通分量*/
int fa_r = find_father(r);
if(fa_l != fa_r){ /*如果不在同一个连通分量*/
fa[fa_l] = fa_r; /*合并!*/
j ++; /*多加一条边*/
_ans += line[i].cost; /*费用*/
array.push_back(i); /*添加最小边*/
}
}
/*二级制枚举子集!*/
for(int i = 1 ; i < (1 << m) ; i++){
LL ans = solve(i);
_ans = min(ans,_ans);
}
printf("%lld\n",_ans);
if(T > 0)
printf("\n");
}
return 0;
}