A*算法正确性的证明
我们首先讨论在具体实际情况下正确性的讨论
example
题意
求有向正边权简单图中点到点的 非严格第k短路 。
做法
首先考虑朴素的dij 的原理。
由于dij每次取出的是目前的最小的点,它不会被别的点更新,所以着一定就是起点到的最短路。
考虑魔改 ,首先发现到的最短路分成一些部分
性质 : 从中间任意选取,都不会用到k+1短路及i短路。
因为用k短路一定更短,用数学归纳法可以简单地证明。
所以我们只用处理前k短路,然后堆优化的dij每次弹出的恰好是里面目前最短的,也就是说每个点第i次被弹出,此时的dis就是第i小值。
然后可以发现每个点被弹出k次后就不用再用于更新其他点了。(性质)
每个点只会被用于处理k次,该算法的时间复杂度是
接下来考虑使用A*算法,即使用估价函数来优化bfs顺序。
由于A*的性质,所以估价函数应小于等于实际从起点走到终点的k短路在x到终点的部分。
所以我们选取估价函数表示x到终点的距离。
将dij中堆的优先级设为即可,其中表示从起点到目前状况的代价即可。
同样,每个点被弹出k次后就不用再用于更新其他点了。
正确性证明:
令表示实际总代价
表示总估价,
表示目前代价,
表示未来估价。
性质
:对于
文字理解:这一步做了最优决策,那么才能等于,其他选择都不优于最优选择,故
:对于拓扑序(不是拓扑序列) 单调不减
由归纳得到。
(一句废话):对于同一个点,若则
即若按照堆的优先级来取,若第短和第短的都在堆里,那么每次取出一定是第大。
由我们只需要证明当第i+1短的出堆时,i短的已经处理过了。
假设由转移来,由转移来。
即使,那么先被放入堆中,但是很明显有 所以在出堆前,会进堆并在出堆前扩展出
使数学归纳法可推广至任意情况。
又因为同一个点的出堆顺序一定是正确的。
所以第次取出的是第小值
证毕
code :
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define min3(xxx,yyy,zzz) min(min(xxx,yyy),zzz)
#define max3(xxx,yyy,zzz) max(max(xxx,yyy),zzz)
#define pii pair<int,int>
#define fi first
#define se second
#define rep(variable,leftrange,rightrange) for(int variable=leftrange;variable<=rightrange;++variable)
#define Rep(variable,leftrange,rightrange) for(int variable=leftrange;variable>=rightrange;--variable)
#define lb long double
#define mem(x,y) memset(x,y,sizeof x)
#define sq(x) ((x)*(x))
#define ss stable_sort
#define rs random_shuffle
#define nxp next_permutation
#define lowbit(x) (x& -x)
#define ll long long
#define lll __int128
#define gi greater<int>
#define vi vector<int>
#define upmin(x,y) x=min(x,y)
#define upmax(x,y) x=max(x,y)
template <typename T>inline void read(T &t)
{int c=getchar();t=0;while(!isdigit(c))c=getchar();while(isdigit(c))t=(t<<3)+(t<<1)+c-48,c=getchar();}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
template <typename T> inline void wrt(T x)
{if(0<=x&&x<10) {putchar(x+'0');return ;}wrt(x/10);putchar(x%10+'0');}
template <typename T> inline void wrt(T x,char c) {wrt(x);putchar(c);}
const int N= 1004 ;
const int M= 100004;
struct edge{
int v,w;
} ;
vector<edge> ed[N],fed[N];
void adde(int u,int v,int w)
{
ed[u].push_back((edge){v,w});
}
void fadde(int u,int v,int w)
{
fed[u].push_back((edge){v,w});
}
int n,m,fdis[N],vis[N],dis[N],cnt[N];
int s,t,k;
priority_queue<pii > q;
void dij()
{
memset(fdis,0x3f,sizeof fdis);
q.push({0,t});
fdis[t]=0;
while(!q.empty())
{
auto np = q.top();
int po = np.second;
// int ndis = -np.fi;
q.pop();
if(vis[po]) continue ;
vis[po]=1;
for(int i=0;i<fed[po].size();++i)
{
int v = fed[po][i].v;
int w = fed[po][i].w;
if(fdis[v]>fdis[po]+w)
{
fdis[v]=fdis[po]+w;
q.push({-(fdis[po]+w),v});
}
}
}
}
struct point{
int p,g,f;
bool operator<(const point &p1) const {
return f>p1.f;
}
};
priority_queue<point> aq;
int A()
{
aq.push((point){s,0,fdis[s]});
while(!aq.empty())
{
point np = aq.top();
aq.pop();
int pos=np.p;
cnt[pos]++;
if(cnt[t]>=k) return np.g;
for(int i=0;i<ed[pos].size();++i)
{
int tp = ed[pos][i] . v;
if(cnt[tp]<k)
aq.push((point){tp,np.g+ed[pos][i].w,np.g+ed[pos][i].w+fdis[tp]});
}
}
return -1;
}
int main()
{
read(n,m);
rep(i,1,m)
{
int u,v,w;
read(u,v,w);
adde(u,v,w);
fadde(v,u,w);
}
read(s,t,k);
if(s==t)k++;
dij();
int ans = A();
cout<<ans;
return 0;
}
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17971000,谢谢你的阅读或转载!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步