题意:平面上有n个点(1<=n<=1000),你的任务是让所有n个点连通。可以新建边,费用等于两端点欧几里德距离的平方。也可以购买套餐(套餐中的点全部连通)。问最小费用。
分析:
1、先将不购买任何套餐的最小生成树的所有边(边数为cnt)存起来,目的是枚举套餐时不必再耗Kruskal算法的O(n2)复杂度,而是降低为O(cnt)。
2、二进制枚举套餐。
3、枚举套餐时,先将套餐中的边按最小生成树建边,在将不购买任何套餐的最小生成树的cnt条边建上,因为套餐中的边权值为0,所以这样处理不会影响结果。
#pragma comment(linker, "/STACK:102400000, 102400000") #include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream> #include<sstream> #include<iterator> #include<algorithm> #include<string> #include<vector> #include<set> #include<map> #include<stack> #include<deque> #include<queue> #include<list> #define Min(a, b) ((a < b) ? a : b) #define Max(a, b) ((a < b) ? b : a) const double eps = 1e-8; inline int dcmp(double a, double b){ if(fabs(a - b) < eps) return 0; return a > b ? 1 : -1; } typedef long long LL; typedef unsigned long long ULL; const int INT_INF = 0x3f3f3f3f; const int INT_M_INF = 0x7f7f7f7f; const LL LL_INF = 0x3f3f3f3f3f3f3f3f; const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f; const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1}; const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1}; const int MOD = 1e9 + 7; const double pi = acos(-1.0); const int MAXN = 1000 + 10; const int MAXT = 10000 + 10; using namespace std; vector<int> v;//不选择套餐时最小生成树的边编号 int ans, n, m; int fa[MAXN]; struct City{//城市 int x, y, id; void read(){ scanf("%d%d", &x, &y); } }num[MAXN]; struct Package{//套餐 int n, cost; int city[MAXN]; void read(){ scanf("%d%d", &n, &cost); for(int i = 0; i < n; ++i){ scanf("%d", &city[i]); } } }q[10]; struct Edge{ int from, to, dist; void set(int f, int t, int d){ from = f; to = t; dist = d; } bool operator < (const Edge& rhs)const{ return dist < rhs.dist; } }e[MAXN * MAXN]; int getD(City& A, City& B){ return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); } int Find(int v){ return fa[v] = (fa[v] == v) ? v : Find(fa[v]); } void solve(){//二进制枚举子集 for(int i = 1; i < (1 << m); ++i){ int tmp = 0; for(int j = 1; j <= n; ++j) fa[j] = j;//初始化并查集 for(int j = 0; j < m; ++j){ if(i & (1 << j)){ tmp += q[j].cost; for(int a = 0; a < q[j].n; ++a){ for(int b = a + 1; b < q[j].n; ++b){ int x = Find(q[j].city[a]); int y = Find(q[j].city[b]); if(x == y) continue; if(x < y) fa[y] = x; else fa[x] = y; } } } } int len = v.size(); for(int j = 0; j < len; ++j){ int id = v[j]; int x = Find(e[id].from); int y = Find(e[id].to); if(x == y) continue; tmp += e[id].dist; if(x < y) fa[y] = x; else fa[x] = y; } ans = Min(ans, tmp); } } int main(){ int T; scanf("%d", &T); while(T--){ scanf("%d%d", &n, &m); for(int i = 0; i < m; ++i){ q[i].read(); } for(int i = 1; i <= n; ++i){ num[i].read(); fa[i] = i; } int cnt = 0; for(int i = 1; i <= n; ++i){ for(int j = i + 1; j <= n; ++j){ e[cnt++].set(i, j, getD(num[i], num[j])); } } sort(e, e + cnt); v.clear(); ans = 0; for(int i = 0; i < cnt; ++i){ int x = Find(e[i].from); int y = Find(e[i].to); if(x == y) continue; ans += e[i].dist; v.push_back(i); if(x < y) fa[y] = x; else fa[x] = y; } solve(); printf("%d\n", ans); if(T) printf("\n"); } return 0; }