题解 P3264【[JLOI2015]管道连接】

$\text{Link}$

题意

给出一张 $n$ 个点,$m$ 条有权边的无向连通图,其中有 $k$ 个点是关键点,每个关键点都有一个颜色 $c_i$。求图的一个子图,满足所有颜色相同的关键点连通,使得所包含的边集的权值和最小,求这个最小值。

$n\le 1000$,$m\le3000$,$c_i\le k\le10$

思路

首先考虑如果所有点都是一个颜色,就是 $\text{P6192}$ 了,可以使用状压 $\text{dp}$ 求出最小斯坦纳树,如果还不会可以看看我的题解

设 $f_S$ 表示集合 $S$ 内的颜色都已连通的答案。

考虑用 $dp_{i,S}$ 为 $f$ 初始化,再枚举子集转移得到答案。初始化时不能只初始化 $f_{\{i\}}$,而是要把所有 $f$ 初始化,否则可能出现重边的情况。

考虑使用 $\text{dfs}(x,stu_p,stu_c)$ 表示搜到第 $x$ 种颜色,前 $x$ 种颜色中关键点集合为 $stu_p$,选中颜色集合为 $stu_c$,搜到终止状态即 $x=k+1$ 时,我们令 $f_{stu_c}=\min_{i=1}^n dp_{i,stu_p}$ 即可。

最后直接枚举子集转移求出答案就做完了。

时间复杂度 $O(nm\times 2^k+n\times 3^k)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1000+10,K=10;
int n,m,k,p[N],up,c[N];
int cnt,head[N];
int dp[N][1<<K],sub[N];
int dp2[1<<K],up2;
struct Edge{
    int to,nxt,w;
}a[N*6];
inline void add(int u,int v,int w){
    cnt++;
    a[cnt].to=v;
    a[cnt].w=w;
    a[cnt].nxt=head[u];
    head[u]=cnt;
}
queue<int>q;
bool vis[N];
inline void SPFA(int s){
    while(!q.empty()){
        int rt=q.front();
        q.pop();
        vis[rt]=0;
        for(int i=head[rt];i;i=a[i].nxt){
            int t=a[i].to;
            if(dp[t][s]>dp[rt][s]+a[i].w){
                dp[t][s]=dp[rt][s]+a[i].w;
                if(!vis[t])
                    q.push(t),vis[t]=1;
            }
        }
    }
}
inline void dfs(int x,int stup,int stuc){
    if(x==k+1){
        for(int i=1;i<=n;i++)
            dp2[stuc]=min(dp2[stuc],dp[i][stup]);
        return ;
    }
    dfs(x+1,stup|sub[x],stuc|(1<<x-1));
    dfs(x+1,stup,stuc);
}
int main(){
    memset(dp,127/3,sizeof(dp));
    memset(dp2,127/3,sizeof(dp2));
    n=read(),m=read(),k=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w); 
    }
    for(int i=1;i<=k;i++){
        c[i]=read(),p[i]=read();
        dp[p[i]][1<<i-1]=0;
        sub[c[i]]|=1<<i-1;
        up2|=1<<c[i]-1;
    }
    up=(1<<k)-1;
    for(int s1=0;s1<=up;s1++){
        for(int i=1;i<=n;i++){
            for(int s2=s1&s1-1;s2;s2=s2-1&s1)
                dp[i][s1]=min(dp[i][s1],dp[i][s2]+dp[i][s1^s2]);
            if(dp[i][s1]<1e9)
                q.push(i),vis[i]=1;
        }
        SPFA(s1);
    } 
    dfs(1,0,0);
    for(int s1=1;s1<=up2;s1++)
        for(int s2=s1;s2;s2=s2-1&s1)
            dp2[s1]=min(dp2[s1],dp2[s2]+dp2[s1^s2]);
    write(dp2[up2]);
    flush();
}

再见 qwq~

posted @ 2021-08-20 11:11  ffffyc  阅读(7)  评论(0编辑  收藏  举报  来源