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.
Sample Input
10 20 1
6 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7
Sample Output
8
题解
斯坦纳树模板题。
设状态(f[i][j])表示:以(i)为根且至少包含了集合(j)中的点的某个连通子树的最小边权和,其中(j)是(S)的一个子集。
(g[j])表示考虑了点集(j)中的点的最优选边方案的边权和,其中(j)是(S)的一个子集。
我们判定(g[j])合法当且仅当要连通的点对同时出现或同时不出现在状态(j)中。
那么(g[j]=mathrm{min}{g[j],g[s]+g[j-s]}),且(s)是合法状态。
这样我们先用斯坦纳树的方法求出(f),从而得到初始的(g).
显然(g[j]=mathrm{min}{f[i][j]})
然后枚举当前状态(j)以及当前状态的合法子集更新(g).
最终(g[S])即为答案((S)是题中给出的点集)。
代码
#include<bits/stdc++.h>
#define MAXN 20010
#define INF 0x3f3f3f3f
namespace IO{
char buf[1<<15],*fs,*ft;
inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int qr(){
int x=0,rev=0,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')rev=1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
return rev?-x:x;}
}using namespace IO;
using namespace std;
struct Edge{int t,next,v;}e[MAXN<<1];
int N,M,D,f[MAXN][1<<8],g[1<<8],head[MAXN],cnt;
inline void Add_Edge(int from,int to,int val){
e[++cnt].t=to;e[cnt].next=head[from];head[from]=cnt;e[cnt].v=val;
e[++cnt].t=from;e[cnt].next=head[to];head[to]=cnt;e[cnt].v=val;
}
queue<int>q;
bool vis[MAXN];
inline void Spfa(int S){
for(int i=1;i<=N;i++)if(f[i][S]<INF)q.push(i),vis[i]=1;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].t;
if(f[u][S]+e[i].v<f[v][S]){
f[v][S]=f[u][S]+e[i].v;
if(!vis[v])q.push(v),vis[v]=1;
}
}
}
}
inline bool Check(int S){
for(int i=0;i<D;i++){
if(((S>>i)&1)&&!((S>>(D+i))&1))return 0;
}
return 1;
}
int x,y,z;
int main(){
#ifndef ONLINE_JUDGE
freopen("bzoj4774.in","r",stdin);
freopen("bzoj4774.out","w",stdout);
#endif
N=qr();M=qr();D=qr();
for(int i=1;i<=M;i++){
x=qr();y=qr();z=qr();
Add_Edge(x,y,z);
}
memset(f,INF,sizeof(f));memset(g,INF,sizeof(g));
for(int i=1;i<=D;i++)f[i][(1<<(i-1))]=f[N-i+1][(1<<(D+i-1))]=0;
for(int i=0,ed=(1<<(D+D));i<ed;i++){
for(int j=1;j<=N;j++){
for(int s=i&(i-1);s;s=i&(s-1)){
f[j][i]=min(f[j][i],f[j][s]+f[j][i-s]);
}
}
Spfa(i);
for(int j=1;j<=N;j++)g[i]=min(g[i],f[j][i]);
}
for(int i=0,ed=(1<<(D+D));i<ed;i++){
for(int s=i&(i-1);s;s=i&(s-1)){
if(Check(s)&&Check(i-s))g[i]=min(g[i],g[s]+g[i-s]);
}
}
printf("%d
",g[(1<<(D+D))-1]==INF?-1:g[(1<<(D+D))-1]);
return 0;
}