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; }