版权声明:转载请注明出处 https://blog.csdn.net/jay__bryant/article/details/81606980
题目大意:给定n个城市(1<=n<=1000),c个联通块(0<=c<=8)。再给定c个连通块的信息:包含cnt个点和价格cost,并给出cnt个点的编号。再给定n个城市的坐标。现在要将n个城市连在一起,可以购买联通块也可以新建边(代价是两城市距离的平方)。求最小代价。
c较小,可以二进制枚举需要添加那些联通块,然后每次kruskal更新答案
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1010;
const int M = 1e6+10;
struct Point
{
int x, y;
int getval(const Point &p)const
{
return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
}
}point[N];
struct Edge
{
int u, v, w;
Edge(){}
Edge(int _u, int _v)
{
u = _u;
v = _v;
w = point[_u].getval(point[_v]);
}
bool operator <(const Edge &e)const
{
return w<e.w;
}
}edge[M];
int tot;
int fa[N];
int n, c;
vector <int> g[10];
int cost[10];
void init_fa()
{
for(int i = 1; i <= n; ++i) fa[i] = i;
}
int findf(int x)
{
return x==fa[x]?x:fa[x]=findf(fa[x]);
}
bool merge_(int x, int y)
{
int f1 = findf(x);
int f2 = findf(y);
if(f1==f2) return 0;
fa[f1] = f2;
return 1;
}
int kruskal()
{
int ret = 0;
int cnt = 0;
for(int i = 1; i <= tot; ++i)
{
if(merge_(edge[i].u, edge[i].v))
{
++cnt;
ret += edge[i].w;
}
if(cnt==n-1) break;
}
return ret;
}
int solve()
{
init_fa();
int ret = kruskal();
for(int i = 0; i<(1<<c); ++i)
{
init_fa();
int sum = 0;
for(int j = 0; j < c; ++j)
{
if(!((i>>j)&1)) continue;
sum += cost[j];
for(int k = 1; k < g[j].size(); ++k)
merge_(g[j][0], g[j][k]);
}
ret = min(ret, sum+kruskal());
}
return ret;
}
int main()
{
scanf("%d %d", &n, &c);
for(int i = 0; i < c; ++i)
{
int cnt;
scanf("%d %d", &cnt, &cost[i]);
for(int j = 1; j <= cnt; ++j)
{
int x;
scanf("%d", &x);
g[i].push_back(x);
}
}
for(int i = 1; i <= n; ++i)
scanf("%d %d", &point[i].x, &point[i].y);
tot = 0;
for(int i = 1; i <= n; ++i)
for(int j = i+1; j <= n; ++j)
edge[++tot] = Edge(i, j);
sort(edge+1, edge+tot+1);
printf("%d\n", solve());
return 0;
}