bzoj4774: 修路

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\n",g[(1<<(D+D))-1]==INF?-1:g[(1<<(D+D))-1]);
	return 0;
}
posted @ 2018-04-14 10:35  lrj998244353  阅读(553)  评论(0编辑  收藏  举报
Live2D