bzoj 4006 [JLOI2015]管道连接(斯坦纳树+状压DP)

 

【题目链接】

 

    http://www.lydsy.com/JudgeOnline/problem.php?id=4006

 

【题意】

 

    给定n点m边的图,连接边(u,v)需要花费w,问将k个点中同颜色的点连接的最小费用。

 

【思路】

 

    题目所求斯坦纳森林。

    如果我们知道满足颜色集合S的最小值g[S],则有递推公式:

        g[S]=min{ g[S’]+g[S-S’] }

    则g[(1<<C)-1]即答案。

    这里的g[S],其实就是一颗包含S中所有颜色的斯坦纳树,即求一棵包含所有颜色在S中的点的斯坦纳树,我们设f[i][st]为在i点且包含点集为st的最小花费则有转移式:

        f[i][st]=min{ f[i][st’]+f[i][st-st’] }

        f[i][st]=min{ f[i’][st]+weight(i,i’) }

    两次状压DP bingo。memset那里可以优化一下,懒得改了 =_=

 

【代码】

 

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 using namespace std;
 12 
 13 typedef long long ll;
 14 const int N = 1e3+10;
 15 const int M = 4e3+10;
 16 const int P = 12;  
 17 const int inf = 0xf0f0f0f;
 18 
 19 ll read() {
 20     char c=getchar();
 21     ll f=1,x=0;
 22     while(!isdigit(c)) {
 23         if(c=='-') f=-1; c=getchar();
 24     }
 25     while(isdigit(c))
 26         x=x*10+c-'0',c=getchar();
 27     return x*f;
 28 }
 29 struct Edge { int v,w,nxt;
 30 }e[M<<1];
 31 int en=1,front[N];
 32 void adde(int u,int v,int w) 
 33 {
 34     e[++en]=(Edge){v,w,front[u]}; front[u]=en;
 35 }
 36 struct Node {
 37     int c,w;
 38     bool operator < (const Node& rhs) const {
 39         return c<rhs.c;
 40     }
 41 }ns[P];
 42 
 43 int n,m,K,cnt=0;
 44 int f[N][1<<P],g[1<<P];
 45 
 46 queue<int> q; int inq[N];
 47 
 48 void spfa(int sta)
 49 {
 50     while(!q.empty()) {
 51         int u=q.front(); q.pop(); inq[u]=0;
 52         trav(u,i) {
 53             int v=e[i].v;
 54             if(f[v][sta]>f[u][sta]+e[i].w) {
 55                 f[v][sta]=f[u][sta]+e[i].w;
 56                 if(!inq[v]) 
 57                     inq[v]=1,q.push(v);
 58             }
 59         }
 60     }
 61 }
 62 int solve()
 63 {
 64     int all=1<<cnt;
 65     FOR(sta,1,all-1) {
 66         FOR(i,1,n) {
 67             for(int s=(sta-1)&sta;s;s=(s-1)&sta) 
 68                 f[i][sta]=min(f[i][sta],f[i][s]+f[i][sta-s]);
 69             if(f[i][sta]!=inf) q.push(i),inq[i]=1;
 70         }
 71         spfa(sta);
 72     }
 73     int ans=inf;
 74     FOR(i,1,n) ans=min(ans,f[i][all-1]);
 75     return ans;
 76 }
 77 
 78 int main()
 79 {
 80     freopen("channel.in","r",stdin);
 81     freopen("channel.out","w",stdout);
 82     n=read(),m=read(),K=read();
 83     FOR(i,1,m) {
 84         int u=read(),v=read(),w=read();
 85         adde(u,v,w); adde(v,u,w);
 86     }
 87     FOR(i,1,K) {
 88         ns[i].c=read(),ns[i].w=read();
 89     }
 90     sort(ns+1,ns+K+1);
 91     int C=0;
 92     FOR(i,1,K) {
 93         if(ns[i].c!=ns[i-1].c) C++;
 94         ns[i].c=C;
 95     }
 96     memset(g,0xf,sizeof(g));
 97     int all=1<<C;
 98     FOR(sta,1,all-1) {
 99         memset(f,0xf,sizeof(f));
100         cnt=0; FOR(i,1,K) if((1<<ns[i].c-1)&sta) f[ns[i].w][1<<cnt++]=0;
101         g[sta]=solve();
102     }
103     FOR(sta,1,all-1) {
104         for(int s=(sta-1)&sta;s;s=(s-1)&sta)
105             g[sta]=min(g[sta],g[s]+g[sta-s]);
106     }
107     printf("%d\n",g[all-1]);
108     return 0;
109 }

 

你可能感兴趣的:(bzoj 4006 [JLOI2015]管道连接(斯坦纳树+状压DP))