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