题解 P3264【[JLOI2015]管道连接】
题意
给出一张 $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~