最小权路径集问题 解题题报告
最小权路径集问题
自闭了,sb斯坦纳树题没看出来,以此纪念..
\(dp_{s,i}\)表示点\(i\)联通关键点集合\(s\)的最小代价,转移显然。
\[dp_{S,i}=\min_{Q+T=S}dp_{Q,i}+dp_{T,i}\\
dp_{S,i}=\min_{E(i,j)\in G}dp_{S,j}+\mathtt {edge_{i,j}}
\]
对\(S\)分层,每层跑最短路即可。
最后再拿个dp合并斯坦纳森林
Code:
#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#include <algorithm>
using std::max;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
int f=0;x=0;char c=gc();
while(!isdigit(c)) f|=c=='-',c=gc();
while(isdigit(c)) x=x*10+c-'0',c=gc();
if(f) x=-x;
}
void ckmin(int &x,int y){x=x<y?x:y;}
const int inf=0x3f3f3f3f;
const int N=105;
int head[N],to[N*N],Next[N*N],edge[N*N],cnt;
void add(int u,int v,int w)
{
to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
}
int n,m,k,dp[1<<10][N],aya[1<<5];
std::queue <int> q;
void spfa(int s)
{
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=head[now];i;i=Next[i])
{
int v=to[i],w=edge[i];
if(dp[s][v]>dp[s][now]+w)
{
dp[s][v]=dp[s][now]+w;
q.push(v);
}
}
}
}
int main()
{
read(n),read(m),read(k);
for(int u,v,w,i=1;i<=m;i++)
{
read(u),read(v),read(w);
add(u,v,w),add(v,u,w);
}
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++)
{
if(i<=k) dp[1<<i-1][i]=0;
dp[0][i]=0;
}
for(int s=1;s<1<<k;s++)
{
for(int i=1;i<=n;i++)
{
for(int t=s;t;t=t-1&s)
ckmin(dp[s][i],dp[t][i]+dp[s^t][i]);
if(dp[s][i]<inf) q.push(i);
}
spfa(s);
for(int i=1;i<=n;i++)
ckmin(dp[s][0],dp[s][i]);
}
memset(aya,0x3f,sizeof aya);
aya[0]=0;
k>>=1;
for(int s=1;s<1<<k;s++)
{
for(int t=s;t;t=t-1&s)
{
int it=0;
for(int i=0;i<k;i++)
if(t>>i&1)
it|=1<<(i<<1),it|=1<<(i<<1|1);
ckmin(aya[s],aya[s^t]+dp[it][0]);
}
}
if(aya[(1<<k)-1]==inf) puts("-1");
else printf("%d\n",aya[(1<<k)-1]);
return 0;
}
2019.5.23