POJ 2249 k短路 洛谷 P2349 金字塔 浅谈Astar


之前一直想写一下 Astar 算法,因为最近考试耽误了些;

简述一下Astar的基本含义:基于BFS与 估价函数 的更优的 BFS;其与普通的BFS的主要区别是Astar具有 更优解优先搜索的顺序性,利用此性质可以解决一些看起来很 无厘头XD 的问题;

既然我们提到,BFS与Astar区别就在于 估价函数(一般称之为 f),那问题很显然就落到了如何设计一个好的 f 的问题;
首先明确一下f的性质并给出简略证明:  
(定义 实际值(ans)为 g)(f=当前代价与未来可能代价)
1. f<=g,且f==g时BFS最易搜得最优解;
         证明:1.很显然 估价函数正确的 估到了 正确答案,那么沿着这个路径一定是正解;(此时f一般为实时更新)
                   2.f<=g : Astar 是以 priorityqueue为基础实现的,则f越大(优)越先被更新,若f>g,则当前是按错误路径搜索
                      因为此时f所代表的路径非最优解(或正解),如若没有判断ans更新,则WA,若判断,那还不如BFS(因为此时                         乱序搜索)
2. f愈趋近于0,越慢,且逐渐不具有 最优解在前性质,此时需要我们增加 判断ans更优的条件(就是BFS了);

对于 f 的个人理解:平衡多种 要求的 更优条件 以达到总体最优(并不是每一个最优);
讲完了基础知识,现在考虑一下k短路;
我们都知道,最短路一遍spfa就好;最长路同理;
那么次短路只需要再最短路基础上牺牲一条min(x边->y边(较大));
次次短路同理;
but,k短路和这个其实没有太大关系XD,不过这给了我们一个想法:
如果我们实时计算出刚刚的min,估计一下这个min可能是那几条边呢?
emm,算法来了:
 很明显我们想知道,到x点花费了多长,和接下来我最小可以花多少,
 这样我更新到end的时候就可以知道我花了多少而且是以一个较短的路线到达的;
 即:f=到当前的代价(g)+到end的最短路(h);//其实g可以去掉,也能跑,这里是为了适应f的实际变化,以达到f<=g(更贴近)以加快效率;
 所以,spfa反向跑一遍最短路得到h ,然后BFS+pq得到g;(ans) //and,,如果f写的极其好,第一次到end极为解(不仅仅是这道题)
 下面贴代码,(emmm,,,不长,不打注释了哈XD)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define M 2000500
#define N 10000
#define INF 0xfffffff
int n,m,k;
int dis[N],vis[N];
int head[M],nxt[M],ver[M],val[M],cnt=0;
int head2[M],nxt2[M],ver2[M];
void add(int x,int y,int c){
	nxt[++cnt]=head[x];
	ver[cnt]=y;
	val[cnt]=c;
	head[x]=cnt;
	nxt2[cnt]=head2[y];	
	ver2[cnt]=x;
	head2[y]=cnt;
	return ;
}
struct node{
	int g,h,f;int to;
	bool friend operator<(node a,node b){
		return a.f>b.f;
	}
};
struct node2{ //acer YY出来的 py 优化 
	int cost,num;
	bool friend operator<(node2 x,node2 y){
		return x.cost>y.cost;
	}
};
void spfa(int s){
	fill(dis+1,dis+1+n,INF);
	memset(vis,0,sizeof(vis));
	vis[s]=1;dis[s]=0;
	node2 now,next;now.cost=0,now.num=s;
	priority_queue<node2>q;q.push(now);
	while(q.size()){
		next=q.top();q.pop();vis[next.num]=0;
		for(int i=head2[next.num];i;i=nxt2[i]){
			if(dis[ver2[i]]>dis[next.num]+val[i]){
				dis[ver2[i]]=dis[next.num]+val[i];
				if(!vis[ver2[i]]){
					vis[ver2[i]]=1;
					now.cost=dis[ver2[i]];
					now.num=ver2[i];
					q.push(now);
				}
	}}}return ;
}
int Astar(int s,int end){
	int tot=0;node e,t;
	priority_queue<node>Q;
	if(s==end)k++;
	if(dis[end]==INF)return -1; 
	e.to=s;e.g=0;e.h=dis[s];e.f=e.g+e.h;
	Q.push(e);
	while(Q.size()){
		e=Q.top();Q.pop();
		if(e.to==end)tot++;if(tot==k)return e.g;
		for(int i=head[e.to];i;i=nxt[i]){
			t.to=ver[i];
			t.g=e.g+val[i];
			t.h=dis[ver[i]];
			t.f=t.g+t.h;
			Q.push(t);
		}
	}return -1;
}
void clean(){
	fill(head+1,head+1+n,0);
	fill(head2+1,head2+1+n,0);	
	cnt=0;
}
int main(void){
	int x,y,w,s,e;
	while(scanf("%d%d",&n,&m)!=EOF){
		clean();
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&x,&y,&w);
			add(x,y,w);
		}
		scanf("%d%d%d",&s,&e,&k);
		spfa(e);
		int ans=Astar(s,e);
		printf("%d\n",ans); 
	}
}
再说洛谷的P2349 金字塔
        lara拿到了金字塔的地图,在她开箱子的时候碰到了机关
,,and放出了一阵使lara逃跑速度减半的毒烟。然后我们的lara开始跑圈;
题中给出的是双向边,好像保证联通来着;(不连通也没事,,lara会飞不成?)
lara想知道,在最坏情况下,花多久跑出去。(想了好久也不知道她知道这个干嘛。。)
         so,,题目简化为,给定一张无向图与st与end ,求一条路,使得其路径上最大路径x2后,其花费最小;
 很明显我们可以发现,题中同时维护了2个条件:1,总花费最小,2,路上max最小;
 那这就很好办了,用f囊括一下,f=到当前的路径和+路上max+到end的最小值
 然后仿着poj2249,Astar一边,完活~ ;)
#include<bits/stdc++.h>
using namespace std;
#define M 100000
#define INF 0x3f3f3f3f
int head[M],nxt[M],ver[M],w[M],cnt=0;
void add(int x,int y,int c){nxt[++cnt]=head[x],head[x]=cnt,ver[cnt]=y,w[cnt]=c;return;}
int dis[M],vis[M];
struct node {
    int f,g,h,num;
    bool friend operator < (node  a,node  b){
        return a.f>b.f;
    }
}; 
void BFS(int s){
    memset(vis,0,sizeof(vis));
    memset(dis,INF,sizeof(dis));
    dis[s]=0;vis[s]=1;
    queue<int>q;q.push(s);
    while(q.size()){
        int now=q.front();q.pop();vis[now]=0;
        for(int i=head[now];i;i=nxt[i]){
            if(dis[ver[i]]>dis[now]+w[i]){
                dis[ver[i]]=dis[now]+w[i];
                if(!vis[ver[i]]){
                    vis[ver[i]]=1;
                    q.push(ver[i]);
                }
            }
        }
    }
}
void Astar(int s,int n){
    node e,t;
    priority_queue<node>Q;
    e.h=0;e.g=0;e.f=e.h;e.num=s;
    Q.push(e);
    while(Q.size()){
        e=Q.top();Q.pop();
        if(e.num==n){
            printf("%d\n",e.g+e.h);
            return;
        }
        for(int i=head[e.num];i;i=nxt[i]){
            t.h=e.h+w[i];
            t.g=max(e.g,w[i]);
            t.f=t.g+t.h+dis[ver[i]];
            t.num=ver[i];
            Q.push(t);
        }
    }
}
int main(void){
    int n,m,x,y,c;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&c);
        add(x,y,c);add(y,x,c);
    }
    BFS(n);
    Astar(1,n);
}

and acer写了一个PY 的队列优化,在BFS,spfa这边很有用的,友链: PY优化XD
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值