1. 程式人生 > >【BZOJ4006】【JLOI2015】管道連接

【BZOJ4006】【JLOI2015】管道連接

span geo com printf rip pop -c script pre

Description

  
  傳送門
  
  
  
  
  

Solution

  
  題目要求相同顏色的點必須在一個連通塊中,但會有多個顏色同屬一個連通塊使得解更優的情況。
  
  想一想DP能否行得通:設\(g_i\)表示已考慮顏色狀態為\(i\)時,最小合法方案的代價。
  
  首先,\(g_i\)可以有一個直觀的初值:由顏色屬於\(i\)的點構建的一棵最小生成樹的邊權和。(初始化)
  
?  接下來,如何考慮兩部分顏色各自的連通塊合起來作為最優解的情況?(兩子集合並更新)
  
?  更新\(g_i\)時,我們枚舉\(i\)的兩個不相交子集\(s_1,s_2\)\(s_1|s_2=i\)

,並用\(g_{s_1}+g_{s_2}\)來更新\(g_i\),即直接嘗試將兩個連通塊取交。不需要擔心兩部分會有重復計算某一條邊的情況,因為這是子集枚舉DP,有邊重復計算的轉移自然不會成為最優轉移,最優的轉移一定會被枚舉到(或者就是初值最優)。
  
?  以上兩種方法並用,就可以順利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】管道連接