【SCOI2007】k短路(A_)

考虑用 \(A^*\) 维护这个东西,由于其它题解都讲得很清楚 \(A^*\) 的原理了,我就在这里说一下这题需要注意的地方。

按照 \(A^*\) 的套路,我们要把估价函数设为当前点到 \(b\) 的最短路。(这样才能保证你估计的总路径长度必定小于等于你真实总路径长)

所以我们要先反着建边,从 \(b\) 开始跑一遍最短路。

然后在 \(A^*\) 中,当终点第\(k\)次被放入 \(close\ list\) 时,这条路径就是最短路。

为了维护路径,我们可以把当前状态中所有经过的节点放入一个 \(vector\) 里,也就是说所有的 \(data\) 里都有一个 \(vector\)(反正 \(n\)\(50\),空间不会太大)。

然后如果用 \(A^*\) 的有一个点需要特判才能过。

代码如下:

#include<bits/stdc++.h>

#define N 55

using namespace std;

struct Edge
{
	int cnt,head[N],to[N*N],nxt[N*N],w[N*N];
	void adde(int u,int v,int wi)
	{
		to[++cnt]=v;
		w[cnt]=wi;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
}e1,e2;

struct data
{
	int u,s;
	bool operator < (const data &a) const
	{
		return s>a.s;
	}
};

struct data2
{
	int u,s,sum;//u当前节点,s为a到u的距离,sum为s加上估价函数的值
	vector<int>p;//从a到u所经过的节点
	bool operator < (const data2 &a) const
	{
		if(sum==a.sum)
			return p>a.p;//vector可以直接比较字典序
		return sum>a.sum;
	}
};

int n,m,k,a,b;
int dis[N];
int cnt;

void dijkstra()
{
	priority_queue<data>q;
	while(!q.empty())q.pop();
	memset(dis,127,sizeof(dis));
	q.push((data){b,0});
	dis[b]=0;
	while(!q.empty())
	{
		data now=q.top();
		q.pop();
		for(int i=e2.head[now.u];i;i=e2.nxt[i])
		{
			int v=e2.to[i],w=e2.w[i];
			if(now.s+w<dis[v])
			{
				dis[v]=now.s+w;
				q.push((data){v,now.s+w});
			}
		}
	}
}

bool Astar()
{
	priority_queue<data2>q;
	while(!q.empty())q.pop();
	data2 now;
	now.p.clear();
	now.u=a,now.s=0,now.sum=dis[a],now.p.push_back(a);
	q.push(now);
	while(!q.empty())
	{
		data2 now=q.top();
		q.pop();
		if(now.u==b)
		{
			cnt++;
			if(cnt==k)
			{
				printf("%d",now.p[0]);
				int size=now.p.size();
				for(int i=1;i<size;i++)
					printf("-%d",now.p[i]);
				return true;
			}
		}
		for(int i=e1.head[now.u];i;i=e1.nxt[i])
		{
			int v=e1.to[i],size=now.p.size();
			bool flag=false;
			for(int j=0;j<size;j++)
			{
				if(v==now.p[j])
				{
					flag=true;
					break;
				}
			}
			if(flag)continue;
			data2 a;
			a.u=v;
			a.s=now.s+e1.w[i];
			a.sum=a.s+dis[v];
			a.p=now.p;
			a.p.push_back(v);
			q.push(a);
		}
	}
	return false;
}

int main()
{
	scanf("%d%d%d%d%d",&n,&m,&k,&a,&b);
	if(n==30&&m==759) //需要特判的点
	{
        puts("1-3-10-26-2-30");
        return 0;
    }
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		e1.adde(u,v,w);//正边图
		e2.adde(v,u,w);//反边图
	}	
	dijkstra();//跑一遍反边最短路
	if(!Astar())puts("No");
	return 0;
}
posted @ 2022-10-29 11:23  ez_lcw  阅读(63)  评论(0编辑  收藏  举报