Uva1151.Buy or Build (最小生成树)
Description
给出n个点 (n≤1000) 个点,以及点的坐标,两点之间连通的代价为其距离的平方。现在还有q种购买方案 (q≤8) ,每一种购买方案的花费为 ci ,购买后就会使得其所包含的点连通。
现在想要求出花费最少,使得图上的点全部连通,输出其最小花费,注意各组输出之间有一个空行。
Solution
显然暴力方法不可取。
考虑最小生成树的算法:
对一个图来说,如果在其最小生成树上加一些边,可能会改变原图的最小生成树。原因十分简单,边的个数会超过 n−1 ,形成一些环。如果对每个环来说,我们去掉那些权值较大的边,生下来的就是现在图的最小生成树。
对于本题而言,购买套餐,就等价于在原来的最小生成树上加了一些权值为0的边,如此一来,只需要对原来的最小生成树+新加入权值为0的边再跑一边克鲁斯卡尔算法即可。
对于不同的套餐选择,可以采用二进制枚举的方法。
Code
#include<bits/stdc++.h>
#define ll long long
#define nmax 600000
#define pmax 1005
using namespace std;
struct edge{
int f,t,w;
}e[nmax],_e[pmax];
struct point{
int x,y;
}p[pmax];
int cost[10],num[10],qq[10][pmax];
int fa[nmax];
int t,n,q,cnt=0,cnt2 = 0,ans = 1000000000,costemp;
void makeset(int x){
fa[x] = x;
}
int findset(int x){
int rt = x,temp;
while(fa[rt] != rt) rt = fa[rt];
while(x != rt){
temp = fa[x];
fa[x] = rt;
x = temp;
}
return rt;
}
int unionset(int x, int y){
x = findset(x);
y = findset(y);
if(x == y) return -1;
else{
fa[x] = y;
return 0;
}
}
bool cmp(edge a, edge b){
return a.w < b.w;
}
int dis(point a , point b){
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
void caledge(){
for(int i = 1;i<=n-1;++i){
for(int j = i+1;j<=n;++j){
e[++cnt].f = i;
e[cnt].t = j;
e[cnt].w = dis(p[i],p[j]);
}
}
}
void solve(){
for(int i = 1;i<cnt2;++i){
if(unionset(_e[i].f,_e[i].t) !=-1){
costemp += _e[i].w;
}
}
}
int main(){
// freopen("out.txt","w",stdout);
scanf("%d",&t);
while(t--){
cnt = 0;
ans = 0;
cnt2 = 1;
scanf("%d %d",&n,&q);
for(int i = 1;i<=q;++i){
scanf("%d %d",&num[i],&cost[i]);
for(int j = 1;j<=num[i];++j) scanf("%d",&qq[i][j]);
}
for(int i = 1;i<=n;++i){
int x, y;
scanf("%d %d",&x,&y);
p[i].x = x; p[i].y = y;
}
caledge();
sort(e+1,e+1+cnt,cmp);
for(int i = 1;i<=n;++i) makeset(i);
for(int i = 1;i<=cnt;++i){
if(unionset(e[i].f,e[i].t) != -1){
ans+=e[i].w;
_e[cnt2++] = e[i];
}
}
for(int s = 0;s<(1<<q);++s){
for(int i = 1;i<=n;++i) makeset(i);
costemp = 0;
for(int j = 0;j<q;++j){
if(1&(s>>j)){ // buy the gift :D
costemp += cost[j+1];
for(int k = 1;k<=num[j+1]-1;++k){
unionset(qq[j+1][k],qq[j+1][k+1]);
}
}
}
solve();
ans = min(ans,costemp);
}
printf("%d\n",ans);
if(t>0) printf("\n");
}
return 0;
}