Buy or Build(最小生成树+状压枚举)
题意:
n个城市,告诉每个城市的坐标,还有q个联通块,现在要把这n个城市连起来,可以购买联通块(每个有一定的费用),或者新建一条边(费用为点之间的距离的平方),问最小费用是多少。
思路:
首先可以想到朴素的做法,二进制枚举每个连通块选还是不选,判断该状态下图是否已经联通,如果未联通的话再从朴素的边里选择,时间复杂度为\(O(2^{q}*n^{2}+n^{2}*log_{n})\)
考虑怎么优化,要理解kruskal的算法本质,就是按照边权排序后先选择小的边,那么我们用连通块去替换已选的边时,只会替换那些原本就存在最小生成树中的边,这样才能保证最优解。
也就是说,我们先对原图做一遍kruskal,并且记录出所有要选择的边的集合记作\(edge1\),然后我们二进制枚举连通块,判断该状态下图是否联通,如果未联通的话就从\(edge1\)里加边,这样就完成了很大的剪枝。时间复杂度为\(O(2^{q}*(n-1)+n^{2}*log_{n})\)
代码:
const int maxn=1e6+7;
struct node{
ll u,v,w;
};
node edge1[500500],edge[500500];
struct node1{
ll m,w;
vector<int>v;
}a[10];
int root[1100],n,q;
PLL pos[1100];
bool cmp(node a,node b){
return a.w<b.w;
}
int Find(int x){
if(x!=root[x]) root[x]=Find(root[x]);
return root[x];
}
void init(int n){
for(int i=1;i<=n;i++) root[i]=i;
}
ll dis(PLL a,PLL b){
return (a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second);
}
void Union(vector<int>v,ll &tot){
for(int i=0;i<v.size();i++){
for(int j=i+1;j<v.size();j++){
int fu=Find(v[i]),fv=Find(v[j]);
if(fu!=fv) root[fu]=fv,tot++;
}
}
}
void solve(){
n=read,q=read;
for(int i=0;i<q;i++) a[i].v.clear();
for(int i=0;i<q;i++){
a[i].m=read,a[i].w=read;
for(int j=1;j<=a[i].m;j++){
int x=read;
a[i].v.push_back(x);
}
}
for(int i=1;i<=n;i++) pos[i].first=read,pos[i].second=read;
ll idx=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
edge[++idx]={i,j,dis(pos[i],pos[j])};
}
}
sort(edge+1,edge+1+idx,cmp);
init(n);
ll cnt=0,ans=0;
for(int i=1;i<=idx;i++){
int u=edge[i].u,v=edge[i].v;
int fu=Find(u),fv=Find(v);
if(fu!=fv){
cnt++;
root[fu]=fv;
edge1[cnt]={u,v,edge[i].w};
ans+=edge[i].w;
}
if(cnt==n-1) break;
}
for(int i=0;i<(1<<q);i++){
ll res=0,tot=0;
init(n);
for(int j=0;j<q;j++)
if((i>>j)&1){
res+=a[j].w;
Union(a[j].v,tot);
}
if(tot==n-1){
ans=min(ans,res);
}
else{
for(int j=1;j<=n-1;j++){
int u=edge1[j].u,tv=edge1[j].v,w=edge1[j].w;
int fu=Find(u),fv=Find(tv);
if(fu!=fv){
tot++;
root[fu]=fv;
res+=w;
}
if(tot==n-1) break;
}
ans=min(ans,res);
}
}
printf("%lld\n",ans);
}
int main() {
int T=read;
while(T--){
solve();
if(T) puts("");
}
return 0;
}