Description
村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。对于边带权的无向图 G = (V, E),
请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边
的权值和。
Input
第一行两个整数 n, m,表示图的点数和边数。接下来的 m行,每行三个整数 ui, vi, wi,表示有一条 ui 与 vi
之间,权值为 wi 的无向边。
1 <= d <= 4
2d <= n <= 10^4
0 <= m <= 10^4
1 <= ui, vi <= n
1 <= wi <= 1000
Output
一行一个整数,表示答案,如果无解输出-1
将要求联通的2d个点及其子集求一次斯坦纳树,最后再用状压dp得出最小权值和
#include<cstdio> #include<cstring> #include<queue> const int N=10007,inf=0x3f3f3f3f; int n,m,d,mx; int es[N*2],enx[N*2],ev[N*2],e0[N],ep=2; int f[256][N],g[16]; struct node{ int w,l; bool operator<(const node&x)const{return l>x.l;} }; std::priority_queue<node>q; void mins(int&a,int b){if(a>b)a=b;} int main(){ scanf("%d%d%d",&n,&m,&d); for(int i=0,a,b,c;i<m;++i){ scanf("%d%d%d",&a,&b,&c); es[ep]=b;enx[ep]=e0[a];ev[ep]=c;e0[a]=ep++; es[ep]=a;enx[ep]=e0[b];ev[ep]=c;e0[b]=ep++; } mx=1<<d; memset(f,0x3f,sizeof(f[0])*mx*mx); memset(g,0x3f,sizeof g); for(int i=1;i<=d;++i)f[1<<i-1][i]=f[1<<i+d-1][n+1-i]=0; for(int i=1;i<mx*mx;++i){ int*l=f[i]; for(int j=i-1&i;j>(i^j);j=j-1&i){ for(int w=1,*l1=f[j],*l2=f[i^j];w<=n;++w)mins(l[w],l1[w]+l2[w]); } for(int w=1;w<=n;++w)if(l[w]<inf)q.push((node){w,l[w]}); while(!q.empty()){ node w=q.top();q.pop(); if(w.l!=l[w.w])continue; for(int e=e0[w.w];e;e=enx[e]){ int u=es[e],d=w.l+ev[e]; if(l[u]>d)q.push((node){u,l[u]=d}); } } if((i&mx-1)==(i>>d)){ int ml=inf; for(int j=1;j<=n;++j)mins(ml,l[j]); mins(g[i>>d],ml); } } for(int i=1;i<mx;++i){ for(int j=i-1&i;j;j=j-1&i)mins(g[i],g[j]+g[i^j]); } printf("%d",g[mx-1]!=inf?g[mx-1]:-1); return 0; }