BZOJ 4774: 修路

Decription

求将\(1...d\)分别与\(n,n-1,...,n-d+1\)连通的最小代价。

\(n\leqslant 10^4,m\leqslant 10^4,d\leqslant 4\)

Solution

斯坦纳树+装压DP。

首先用斯坦纳树处理出,每个点的与几个关键点连通状态的最小代价。

然后装压DP。

一开始写的比较暴力,枚举集合,再枚举子集,在枚举点,再枚举集合。

复杂度是什么\(O(nd3^d2^{2d})\)

然后把那个什么枚举点和枚举集合放在外面枚举就行了...

复杂度\(O(n3^{2d}+nd2^{2d}+3^d)\)

跑得好慢啊 =w=.

Code

#include <bits/stdc++.h>
using namespace std;

#define mpr make_pair

inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }

typedef pair<int,int> pr;
const int N = 10050;
const int K = 1<<8;
const int oo = 0x3f3f3f3f;

int n,m,d;
int f[N][K],ff[K];
int b[N];
vector<pr> g[N];
queue<int> q;

int idx(int x) {
	if(x<=d) return x;
	if(x<=n-d) return x+d;
	return x-n+d+d;
}
void AddEdge(int x,int y,int w) { g[x].push_back(mpr(y,w)); }

void SPFA(int S) {
	for(int u;!q.empty();) {
		u=q.front(),q.pop(),b[u]=0;
		for(int i=0;i<(int)g[u].size();i++) {
			int v=g[u][i].first,w=g[u][i].second;
			if(f[u][S]+w<f[v][S]) {
				f[v][S]=f[u][S]+w;
				if(!b[v]) q.push(v),b[v]=1;
			}
		}
	}
}
int chk(int j,int S) {
	for(int i=0;i<d;i++) if((1<<i)&S) {
		if(((j&(1<<i))==0) || ((j&(1<<(d+d-i-1)))==0)) return 0;
	}return 1;
}
int cal(int S) {
	int r=0;
	for(int i=0;i<d;i++) {
		if(((S&(1<<i))==0) || ((S&(1<<(d+d-i-1)))==0)) continue;
		r|=1<<i;
	}return r;
}
int Solve() {
	d<<=1;
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=d;i++) f[i][1<<(i-1)]=0;
	for(int S=0;S<(1<<d);S++) {
		for(int i=1;i<=n;i++) for(int j=S;j;j=(j-1)&S)
			f[i][S]=min(f[i][S],f[i][j]+f[i][S^j]);
		for(int i=1;i<=n;i++) if(f[i][S]<oo) q.push(i),b[i]=1;
		SPFA(S);
	}
	memset(ff,0x3f,sizeof(ff));
	ff[0]=0,d>>=1;
	for(int i=1;i<=n;i++) 
		for(int j=0;j<(1<<(d*2));j++) {
			int t=cal(j);
			ff[t]=min(ff[t],f[i][j]);
		}
	for(int S=0;S<(1<<d);S++) for(int T=S;;T=(T-1)&S) {
		ff[S]=min(ff[S],ff[S^T]+ff[T]);
		if(T==0) break;
	}
	return ff[(1<<d)-1]==oo?-1:ff[(1<<d)-1];
}

int main() {
	n=in(),m=in(),d=in();
	for(int i=1;i<=m;i++) {
		int x=in(),y=in(),w=in();
		x=idx(x),y=idx(y);
		AddEdge(x,y,w),AddEdge(y,x,w);
	}
	printf("%d\n",Solve());
	return 0;
}

  

posted @ 2017-03-22 16:28  北北北北屿  阅读(184)  评论(0编辑  收藏  举报