1. 程式人生 > >bzoj4006 [JLOI2015]管道連線

bzoj4006 [JLOI2015]管道連線

題目描述

題解:

由於$p<=10$,我們可以暴力列舉覆蓋頻道狀態為$s$的特殊點,每次跑一遍斯坦納樹,

最後合併,方程和斯坦納樹的狀態合併方程幾乎一樣。

程式碼:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1050
#define M 3050
#define ll long long
const ll Inf = 0x3f3f3f3f3f3f3f3fll;
inline int
rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,m,p,q,hed[N],cnt; struct EG { int to,nxt,w; }e[2*M]; void ae(int f,int t,int w) { e[++cnt].to = t; e[cnt].nxt
= hed[f]; e[cnt].w = w; hed[f] = cnt; } int ve[12][12],ct[12]; ll tmp[N][1<<10],g[1<<10]; bool vis[N]; queue<int>que; void spfa(int s) { while(!que.empty()) { int u = que.front();que.pop(); for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to;
if(tmp[to][s]>tmp[u][s]+e[j].w) { tmp[to][s]=tmp[u][s]+e[j].w; if(!vis[to])vis[to]=1,que.push(to); } } vis[u]=0; } } void sol(int k) { memset(tmp,0x3f,sizeof(tmp)); int now = 0; for(int i=0;i<q;i++) if(k&(1<<i)) for(int j=0;j<ct[i+1];j++) tmp[ve[i+1][j]][1<<now]=0,now++; for(int s=1;s<(1<<now);s++) { for(int i=1;i<=n;i++) { for(int t=s&(s-1);t;t=s&(t-1)) { tmp[i][s]=min(tmp[i][s],tmp[i][t]+tmp[i][s^t]); } if(tmp[i][s]!=Inf)que.push(i),vis[i]=1; } spfa(s); } for(int i=1;i<=n;i++) if(tmp[i][(1<<now)-1]<g[k]) g[k]=tmp[i][(1<<now)-1]; } int main() { n = rd(),m = rd(),p = rd(); for(int f,t,w,i=1;i<=m;i++) { f=rd(),t=rd(),w=rd(); ae(f,t,w),ae(t,f,w); } for(int f,t,i=1;i<=p;i++) { f=rd(),t=rd(); ve[f][ct[f]]=t;q+=(!ct[f]); ct[f]++; } memset(g,0x3f,sizeof(g)); for(int s=1;s<(1<<q);s++) { sol(s); for(int t=s&(s-1);t;t=s&(t-1)) g[s]=min(g[s],g[t]+g[s^t]); } printf("%lld\n",g[(1<<q)-1]); return 0; }