D145 最小生成树 Kruskal 算法 P14362 [CSP-S 2025] 道路修复

D145 最小生成树 Kruskal 算法 P14362 [CSP-S 2025] 道路修复_哔哩哔哩_bilibili

 

P14362 [CSP-S 2025] 道路修复 - 洛谷

n 个城市,m 条边,k 个乡镇,每个乡镇都可以改造,都可以与 n 个城市直接连边,求连通 n 个城市的最小费用。

最大 $n=10^4,m=10^6,k=10$

思路

该选择哪些乡镇改造,又连哪些边呢?

$k$ 只有 $10$,可以状态枚举,从 $0$ 到 $2^k-1$,例如,$(00001011)_2$ 表示选择乡镇 $1,2,4$

对于 $m$ 条边,先跑一遍 Kruskal,挑出 $n-1$ 条最小边,其它无用。时间复杂度为 $O(mlog(m))$

然后,把乡镇与城市的 $kn$ 条连边加进来,对 $n-1+kn$ 条边做个预排序。时间复杂度为 $O(knlog(kn))$

每次状态枚举,跑一遍 Kruskal,累加边权,注意去掉没选的乡镇。不用排序了,时间复杂度为 $O(2^k*kn)$

从每次结果中选出最小值,得出最终答案,图中的最小费用 $=1+1+2+4+5=13$

image

 相关板子:

D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园

 

// 最小生成树+状态枚举 O(mlogm+knlogkn+2^k*kn)
#include<bits/stdc++.h>
using namespace std;

int read(){
  int f=1,x=0; char c=getchar();
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) x=10*x+c-'0';
  return f*x;
}
const int N=10005,M=1000005;
struct E{int u,v,w;}e[M]; //边集
int fa[N],c[12],vis[12];

int find(int u){
  return fa[u]==u?u:fa[u]=find(fa[u]);
}
int main(){
  int n=read(),m=read(),k=read();
  for(int i=1;i<=m;i++)e[i]={read(),read(),read()};
  
  sort(e+1,e+1+m,[&](E a,E b){return a.w<b.w;});
  for(int i=1;i<=n;i++) fa[i]=i;
  int tot=0;
  for(int i=1;i<=m;i++){
    int u=find(e[i].u),v=find(e[i].v);
    if(u!=v){
      fa[v]=u;
      e[++tot]=e[i];
      if(tot==n-1) break;
    }
  } //Kruskal
  
  for(int i=1;i<=k;i++){
    c[i]=read();
    for(int j=1;j<=n;j++)e[++tot]={n+i,j,read()};
  } //连城市到乡镇的边
  
  sort(e+1,e+1+tot,[&](E a,E b){return a.w<b.w;});
  long long ans=1e18;
  for(int st=0;st<(1<<k);st++){ //枚举k个点的选择状态
    long long sum=0; int num=0;
    for(int i=1;i<=k;i++){ //1011:选第1,2,4乡镇
      if((st>>(i-1))&1){
        ++num; //选择乡镇的个数
        vis[i]=1; //选择乡镇i
        sum+=c[i]; //加上点权
      }
      else vis[i]=0;
    }
    
    for(int i=1;i<=n+k;i++) fa[i]=i;
    int cnt=0;
    for(int i=1;i<=tot;i++){ //tot=n-1+kn
      int u=e[i].u,v=e[i].v;
      if(u>n && !vis[u-n]) continue; //乡镇u没选
      u=find(u),v=find(v);
      if(u!=v){
        fa[v]=u;
        sum+=e[i].w; //累加边权
        if(++cnt==n+num-1) break;
      }
    }
    ans=min(ans,sum); //各种方案中的最小值
  }
  cout<<ans;
}

 

posted @ 2026-04-28 08:28  董晓  阅读(137)  评论(0)    收藏  举报