紫薯上的原题(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;
}