论坛中我的总结:
这题用状压DP实现。
我们先对于起点和终点,以及朋友家每个跑一遍spfa,将这些点定义为关键点,两两之间都求出了最短路,然后我们可以用状压DP来解决问题。
我们可以设f[i][j]表示当前到达点i,走过朋友家的状态为j的最短路。这是从1到n的!
然后我们再设g[i][j]表示当前到达点j,走过朋友家的状态为j的最短路。这是从n到1的!
而f的初始值为:f[0][0]=0;(0表示起点,k+1表示终点)
g的初始值为:g[k+1]=f[k+1];
最后的答案在g[0]中找即可。
这题就是个状压DP,我们设:
f[i][j]表示当前在第i个朋友家,走到过的状态为j的最小时间。
这是从1走到n的!!!
再设g[i][j]表示当前在第i个朋友家,都到过的状态为j的最小时间。
这是从n返回1的!!!
做完f后就把f[k]赋值为g[k],然后在照做一遍就可以了。
我们可以将第0个朋友表示为1,第k+1个朋友表示为n。
上标:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10010
#define M 50010
#define ll long long
using namespace std;
struct node{int v,fr,l;}e[M<<2];
int n,m,K,t,tail[N],cnt=0,F[N<<3],fd[17],x,ans=0;
ll d[N],dis[17][17],f[17][70010],g[17][70010],time;
bool bz[N];
inline int read()
{
int x=0; char c=getchar();
while (c<'0' || c>'9') c=getchar();
while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
inline void add(int u,int v,int l) {e[++cnt]=(node){v,tail[u],l}; tail[u]=cnt;}
void spfa(int x)
{
memset(d,60,sizeof(d));
int l=0,r=1;F[1]=x;d[x]=0;
while (l++<r)
{
for (int p=tail[F[l]],v;p;p=e[p].fr)
{
if (d[F[l]]+e[p].l>=d[v=e[p].v]) continue;
d[v]=d[F[l]]+e[p].l;
if (!bz[v]) bz[v]=1,F[++r]=v;
}
bz[F[l]]=0;
}
}
int change(int x)
{
int s=0;
while (x>0) s+=(x & 1),x>>=1;
return s;
}
int main()
{
freopen("shu.in","r",stdin);
freopen("shu.out","w",stdout);
n=read(),m=read(),K=read(),t=read();
for (int i=1,u,v,l;i<=m;i++)
{
u=read(),v=read(),l=read();
add(u,v,l),add(v,u,l);
}
for (int i=1;i<=K;i++) fd[i]=read();
fd[0]=1;fd[++K]=n;
for (int i=0;i<=K;i++)
{
spfa(fd[i]);
for (int j=0;j<=K;j++)
dis[i][j]=(i==j) ? 0:d[fd[j]];
}
/*
for (int i=0;i<=K;i++)
{
for (int j=0;j<=K;j++)
printf("%d ",dis[i][j]);
puts("");
}
*/
memset(f,60,sizeof(f));f[0][0]=0;
for (int j=0;j<=(1<<K)-1;j++)
for (int i=0;i<=K;i++)
{
if (f[i][j]>t) continue;
for (int k=1;k<=K;k++)
{
x=j | (1<<k-1);
f[k][x]=min(f[k][x],f[i][j]+dis[i][k]);
}
}
memset(g,60,sizeof(g));
for (int i=0;i<=(1<<K)-1;i++)
g[K][i]=f[K][i];
for (int j=0;j<=(1<<K)-1;j++)
for (int i=K;i>=0;i--)
{
if (g[i][j]>t) continue;
for (int k=0;k<=K;k++)
{
if (!k) x=j;
else x=j | (1<<k-1);
g[k][x]=min(g[k][x],g[i][j]+dis[i][k]);
}
}
for (int i=0;i<=(1<<K)-1;i++)
if (g[0][i]<=t)
{
x=change(i);
if (ans>x) continue;
if (ans<x) ans=x,time=g[0][i];
if (time>g[0][i]) time=g[0][i];
}
if (ans==0) puts("-1");
else printf("%d %lld
",ans-1,time);
return 0;
}