[BZOJ4006][JLOI2015]管道連線-最小斯坦納樹-動態規劃
管道連線
Description
小銘銘最近進入了某情報部門,該部門正在被如何建立安全的通道連線困擾。
該部門有 n 個情報站,用 1 到 n 的整數編號。給出 m 對情報站 ui;vi 和費用 wi,表示情
報站 ui 和 vi 之間可以花費 wi 單位資源建立通道。
如果一個情報站經過若干個建立好的通道可以到達另外一個情報站,那麼這兩個情報站就
建立了通道連線。形式化地,若 ui 和 vi 建立了通道,那麼它們建立了通道連線;若 ui 和 vi 均與 ti 建立了通道連線,那麼 ui 和 vi 也建立了通道連線。
現在在所有的情報站中,有 p 個重要情報站,其中每個情報站有一個特定的頻道。小銘銘
面臨的問題是,需要花費最少的資源,使得任意相同頻道的情報站之間都建立通道連線。
Input
第一行包含三個整數 n;m;p,表示情報站的數量,可以建立的通道數量和重要情報站的數量。接下來 m 行,每行包含三個整數 ui;vi;wi,表示可以建立的通道。最後有 p 行,每行包含兩個整數 ci;di,表示重要情報站的頻道和情報站的編號。
Output
輸出一行一個整數,表示任意相同頻道的情報站之間都建立通道連線所花費的最少資源總量。
Sample Input
5 8 4
1 2 3
1 3 2
1 5 1
2 4 2
2 5 1
3 4 3
3 5 1
4 5 1
1 1
1 2
2 3
2 4
Sample Output
4
HINT
選擇 (1; 5); (3; 5); (2; 5); (4; 5) 這 4 對情報站連線。
對於 100% 的資料,0 < ci <= p <= 10; 0 < ui;vi;di <= n <= 1000; 0 <= m <= 3000; 0 <= wi <=20000。
第一次寫最小斯坦納樹……
這麼經典的姿勢居然之前從未遇到過……
思路:
最終答案一定是若干個聯通塊,每個聯通塊包含若干種頻道的點。
於是狀壓,設代表選擇的頻道集合為時,最優的方案。
可以發現,
其中,代表對應頻道在集合內的所有重要情報站共同構成一個聯通塊時的最優方案。
考慮如何計算。
可以發現,問題即給定一個點集,求原圖的一棵生成樹滿足中的點聯通的同時,總邊權最小。
這就是最小斯坦納樹解決的問題。
最小斯坦納樹同樣需要狀壓。
設表示以為根,已經聯通的點集為的最小代價。
轉移有兩種。
第一種為合併狀態:
第二種為移動根節點,這部分採用spfa轉移:
其中為到的邊權。
最後可知最優方案。
於是這就做完了~
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
const int P=19;
const int N=1009;
const int M=3009;
const int K=(1<<10)+9;
const int Inf=2139062143;
int n,m,p;
int c[P],d[P],ha[P],htop;
int to[M<<1],nxt[M<<1],w[M<<1],beg[N],tot;
int f[K][N],dp[K],inq[N];
queue<int> q;
inline bool chkmin(int &a,int b){if(a>b){a=b;return 1;}return 0;}
inline void add(int u,int v,int c)
{
to[++tot]=v;
nxt[tot]=beg[u];
w[tot]=c;
beg[u]=tot;
}
inline void spfa(int *f)
{
while(!q.empty())
{
int u=q.front();q.pop();inq[u]=0;
for(int i=beg[u];i;i=nxt[i])
if(chkmin(f[to[i]],f[u]+w[i]) && !inq[to[i]])
q.push(to[i]),inq[to[i]]=1;
}
}
inline int calc(int st)
{
int cnt=0,ans=Inf;
memset(f,127,sizeof(f));
for(int i=1;i<=p;i++)
if(st&(1<<c[i]-1))
f[1<<(cnt++)][d[i]]=0;
for(int i=1,e=1<<cnt;i<e;i++)
{
for(int j=(i-1)&i;j;j=(j-1)&i)
for(int k=1;k<=n;k++)
chkmin(f[i][k],f[j][k]+f[i^j][k]);
for(int j=1;j<=n;j++)
if(f[i][j]<Inf && !inq[j])
q.push(j),inq[j]=1;
spfa(f[i]);
}
for(int i=1;i<=n;i++)
chkmin(ans,f[(1<<cnt)-1][i]);
return ans;
}
int main()
{
n=read();m=read();p=read();
for(int i=1,u,v,c;i<=m;i++)
{
u=read();v=read();c=read();
add(u,v,c);add(v,u,c);
}
for(int i=1;i<=p;i++)
ha[++htop]=c[i]=read(),d[i]=read();
sort(ha+1,ha+htop+1);
htop=unique(ha+1,ha+htop+1)-ha-1;
for(int i=1;i<=p;i++)
c[i]=lower_bound(ha+1,ha+htop+1,c[i])-ha;
memset(dp,127,sizeof(dp));
for(int i=1,e=1<<htop;i<e;i++)
{
dp[i]=calc(i);
for(int j=(i-1)&i;j;j=(j-1)&i)
chkmin(dp[i],dp[j]+dp[i^j]);
}
printf("%d\n",dp[(1<<htop)-1]);
return 0;
}