【NOIP2014模拟11.1B组】蜀传之单刀赴会

论坛中我的总结:
这题用状压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;
}
转载需注明出处。
原文地址:https://www.cnblogs.com/jz929/p/11817584.html