这题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; }
1151 - Buy or Build(二进制枚举子集 + 并查集),布布扣,bubuko.com
时间: 2024-10-07 02:17:12