【BZOJ4006】【JLOI2015】管道連接
阿新 • • 發佈:2018-06-30
span geo com printf rip pop -c script pre ,並用\(g_{s_1}+g_{s_2}\)來更新\(g_i\),即直接嘗試將兩個連通塊取交。不需要擔心兩部分會有重復計算某一條邊的情況,因為這是子集枚舉DP,有邊重復計算的轉移自然不會成為最優轉移,最優的轉移一定會被枚舉到(或者就是初值最優)。
? 以上兩種方法並用,就可以順利DP出\(g\)。
? 答案就是\(g_{all}\)。\(all\)是包含所有顏色的集合。
? 對於\(g\)的初值,直接用斯坦納樹計算所有關鍵點的不同組合的最小生成樹即可。
Description
傳送門
Solution
題目要求相同顏色的點必須在一個連通塊中,但會有多個顏色同屬一個連通塊使得解更優的情況。
想一想DP能否行得通:設\(g_i\)表示已考慮顏色狀態為\(i\)時,最小合法方案的代價。
首先,\(g_i\)可以有一個直觀的初值:由顏色屬於\(i\)的點構建的一棵最小生成樹的邊權和。(初始化)
? 接下來,如何考慮兩部分顏色各自的連通塊合起來作為最優解的情況?(兩子集合並更新)
? 更新\(g_i\)時,我們枚舉\(i\)的兩個不相交子集\(s_1,s_2\)且\(s_1|s_2=i\)
? 以上兩種方法並用,就可以順利DP出\(g\)。
? 答案就是\(g_{all}\)。\(all\)是包含所有顏色的集合。
? 對於\(g\)的初值,直接用斯坦納樹計算所有關鍵點的不同組合的最小生成樹即可。
Code
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N=1005,M=3005,C=10,INF=1e9;
int n,m,p,col[N],tin[N][2],colst[C+1];
int idcnt,id[N],rep[C+1];
int clis[C+1],ccnt;
int f[N][1<<C],g[1<<C];
queue<int> q;
bool inq[N];
int h[N],tot;
struct Edge{int v,w,next;}e[M*2];
inline int bit(int i){return !i?0:(1<<(i-1));}
inline bool in(int st,int i){return (st>>(i-1))&1;}
inline void addEdge(int u,int v,int w){
e[++tot]=(Edge){v,w,h[u]}; h[u]=tot;
e[++tot]=(Edge){u,w,h[v]}; h[v]=tot;
}
void read(){
scanf("%d%d%d",&n,&m,&p);
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
}
for(int i=1,c,u;i<=p;i++){
scanf("%d%d",&c,&u);// c ?
tin[i][0]=c; tin[i][1]=u;
clis[++ccnt]=c;
}
sort(clis+1,clis+1+ccnt);
ccnt=unique(clis+1,clis+1+ccnt)-clis-1;
for(int i=1,u,c;i<=p;i++){
c=lower_bound(clis+1,clis+1+ccnt,tin[i][0])-clis;
u=tin[i][1];
col[u]=c;
id[u]=++idcnt;
rep[c]=u;
colst[c]|=bit(id[u]);
}
}
void spfa(int st){
while(!q.empty()){
int u=q.front(); q.pop();
inq[u]=false;
for(int i=h[u],v;i;i=e[i].next){
v=e[i].v;
int ns=st|bit(id[v]);
if(f[u][st]+e[i].w<f[v][ns]){
f[v][ns]=f[u][st]+e[i].w;
if(!inq[v]){
inq[v]=true;
q.push(v);
}
}
}
}
}
void steinerTree(){
int all=1<<p;
for(int i=1;i<=n;i++){
for(int j=0;j<all;j++) f[i][j]=INF;
f[i][bit(id[i])]=0;
}
for(int j=1;j<all;j++){
for(int i=1;i<=n;i++)
if(!(id[i]&&!in(j,id[i]))){
for(int sub=(j-1)&j;sub;sub=(sub-1)&j){
int x=sub|bit(id[i]),y=(j^sub)|bit(id[i]);
if(f[i][x]+f[i][y]<f[i][j])
f[i][j]=f[i][x]+f[i][y];
}
if(f[i][j]!=INF)
q.push(i),inq[i]=true;
}
spfa(j);
}
}
void solve(){
int all=1<<ccnt;
for(int i=1;i<all;i++){
int st=0,rt=0;
for(int j=1;j<=ccnt;j++)
if(in(i,j))
st|=colst[j],rt=rep[j];
g[i]=f[rt][st];
for(int sub=(i-1)&i;sub;sub=(sub-1)&i)
g[i]=min(g[i],g[sub]+g[i^sub]);
}
printf("%d\n",g[all-1]);
}
int main(){
read();
steinerTree();
solve();
return 0;
}
【BZOJ4006】【JLOI2015】管道連接