You had me at hello.|

_Youngxy

园龄:3年7个月粉丝:5关注:28

「POJ2449」第 K 短路 题解报告

1 题面

给定一张N个点,M条边的有向图,求从S到T的第K短路的长度,路径允许重复经过点或边。

输入

第一行包含两个整数N和M (1 <= N <= 1000, 0 <= M <= 100000)。车站编号从1到N,每M条线路包含3个整数A、B、T (1 <= A, B <= N, 1 <= T <= 100)。表明在时间为T时,从第A站到第B站存在一条有向边。

最后一行由三个整数S, T和K (1 <= S, T <= N, 1 <= K <= 1000)组成。

输出

由单个整数组成的一行:使用第k条最短路径欢迎Uyuw公主的长度(所需时间)。如果第k条最短路径不存在,则输出“-1”(不带引号)。

样例

样例输入1

2 2
1 2 5
2 1 4
1 2 2

样例输出1

14

2 分析题面

2.1 题目简述

求s点到t点的第k短路径

2.2 思路

朴素算法

用Dijkstra反复执行,t点在堆中第k次取出的结果

优化

启发式搜索(HEURISTIC SEARCH)A*算法

Djikstra 的h(估价函数)版本

为了对未来产生的代价进行估计,设计一个估价函数,

能够预估任意状态到目标状态的

未来所需代价的估计值

这个估计值就需要用估价函数来计算

那估价函数怎么设计呢?

根据A*估价函数的设计原则

x-tT的估计距离应当不大于x->t的实际距离

所以我们可以这样做:

先用dijkstra求出每个点到t的最短距离,作为估价函数

这样既符合题意,又方便求解

不过现在堆里面存的东西不太一样,

现在堆中存的时从起点到当前节点已经花费的代价+从当前节点到终点的最短路径

3 代码实现(注释)

3.1定义

图的几个数组(结构体也可)以及另外两个结构体,分别供dij和A*用(结构体内要写重载运算符来实现优先队列)

3.2 输入

由于dijkstra是算的从t(终点)到各点的距离,所以存边的时候也要倒着存

然而A*是正着跑的,所以要存一正一反两次图

scanf("%d%d",&n,&m);//n个点,m条边  
for(int i=1;i<=m;i++){
	scanf("%d%d%d",&x[i],&y[i],&z[i]);
	add(y[i],x[i],z[i]);//存反向图 
}
scanf("%d%d%d",&s,&t,&k);//起点s,终点t,K短路  
dij();//得出"反向"最短距离
cnt=0;
memset(fir,0,sizeof(fir));//初始化
for(int i=1;i<=m;i++){
	add(x[i],y[i],z[i]);//存原图
}

3.3 计算

易错点

if(s==t) k++;//起点和终点相同时,k要++
//因为如果s==t,0这条路不能算在K短路中,所以需要求K+1短路

小优化(A*里的)

(本来想写堆的,但是大家都会就没必要写了)

if(tot[u.id]>k) continue;
//如果当前想拓展的点cnt>k就没必要拓展了
//因为这个点已经是求到k+1短路了 
//从这个点继续往下搜肯定得到的是大于等于k+1短路的路径

3. 4 输出

直接输出A*跑出来的结果即可

printf("%d",A());//输出

3.5 总体代码

#include<bits/stdc++.h>//万能头
using namespace std;
const int N=2*100002;
const int INF=2147483647;//尽可能大
int w[N],cnt,to[N],tot[N],nxt[N],fir[N],dis[N],x[N],y[N],z[N];
//dis:终点到各点的距离 x/y/z:建边两次用
// w/to/fir/nxt:图  tot:出队次数 cnt:建边用
bool vis[N];//标记
int n,m,t,s,k;
struct node {
    int d,id;
    //结构体初始化函数
    node(int td,int tid){d=td;id=tid;} //结构体初始化函数
    //重载运算符
    bool operator < (const node& other) const{
        return d > other.d;
    }
};
//node供dijkstra算法使用
struct edge{
	int id,h,g;//g:s到当前点的距离  h:t到当前点的距离 
	//id表示当前点(的排号or标号) 
	edge(int a,int b,int c){id=a;h=b;g=c;}  //结构体初始化函数
    //重载运算符
	bool operator < (const edge& other) const{
        return h+g > other.g+other.h;
    } //优先选择f值小的点 A*算法精髓所在 
}; 
void add(int x,int y,int z){
	w[++cnt]=z;//权值 
	to[cnt]=y;//下个节点 
	nxt[cnt]=fir[x];//下条边的位置 
	fir[x]=cnt;//记录标号
}//邻接表建边 
void dij(){
	//dij算法求终点到各点的距离 用于估价函数h
	//利用反向边确定估价函数h(x)
	memset(vis,0,sizeof(vis));//初始化
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	}//初始化
	dis[t]=0;//初始化t到t的距离为0
	priority_queue<node> Q;//优先队列
    //小根堆
	Q.push(node(0,t));//d = 0 ,id =s  入队
	while(!Q.empty()){
		node u=Q.top();
		Q.pop();
        //若某个点已经被更新到最优,就不用再次更新其他点
		if(vis[u.id]) continue;
		vis[u.id]=1;
        //从u.id 开始遍历边
		for(int i=fir[u.id];i;i=nxt[i]){//松弛
			int v=to[i],val=w[i];
			if(u.d+val<dis[v]){
				dis[v]=u.d+val;
                //d = dis[v],id =v  入队
				Q.push(node(dis[v],v));
			}
		}
	}
}//Dijkstra堆优化版
int A(){//A*求第k短路 
//启发性搜索决定了先后搜索出来的是:最短、次短、第三短...第k短
	priority_queue<edge> Q;
	 //优先队列 优先选择f值小的点 跑正向图
	Q.push(edge(s,0,dis[s]));
    //id=s,h=0,g=dis[s]
	while(!Q.empty()){
		edge u=Q.top();//当前节点 
		Q.pop();//每次取估价函数值最小的节点
		tot[u.id]++;//计算出队次数
		if(tot[u.id]==k&&u.id==t) return u.h+u.g;
		//找到第K短路 返回
		//第几次出队,出队时的距离就是最几短距离
		if(tot[u.id]>k) continue;//cnt>k没必要拓展
		for(int i=fir[u.id];i;i=nxt[i]){//相连的点入队列
			int v=to[i];
			Q.push(edge(v,u.h+w[i],dis[v]));
            //id=v,h=u.h+w[i],g=dis[v]
		}
	}
	return -1;//不存在第k短路 输出-1
}
int main(){
	scanf("%d%d",&n,&m);//n个点,m条边  
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x[i],&y[i],&z[i]);//输入
		add(y[i],x[i],z[i]);//原
	}
	scanf("%d%d%d",&s,&t,&k);//起点s,终点t,K短路  
	if(s==t) k++;//起点和终点相同,k++
	dij();//dijkstra"反向"最短距离
	cnt=0;//清0
	memset(fir,0,sizeof(fir));//初始化
	for(int i=1;i<=m;i++){
		add(x[i],y[i],z[i]);//反向图
	}
	printf("%d",A());//输出
	return 0;
}
//最短路跑spfa也行

4 总结

  1. 本题就是A*的版子题,对于A *的h(x)估计函数设计应该会有一个大概的理解。
  2. 在A*中的估计函数中如果要跑最短路就要从终点跑。。。(建边时也要存反向图)
  3. 相似题目: [SDOI2010] 魔法猪学院 / [SCOI2007]k短路

本文作者:Yvette的博客

本文链接:https://www.cnblogs.com/yvette1217/p/16132139.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Youngxy  阅读(71)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示