A*算法正确性的证明
我们首先讨论在具体实际情况下正确性的讨论
example
题意
求有向正边权简单图$G$中点$s$到点$t$的 非严格第k短路 。
做法
首先考虑朴素的dij 的原理。
由于dij每次取出的是目前的$dis$最小的点,它不会被别的点更新,所以着一定就是起点到$u$的最短路。
考虑魔改$dij$ ,首先发现$s$到$t$的最短路分成一些部分
$$ u_1\to u_2\to\dots \to u $$ 性质 :$p1$ 从中间任意选取$u1$,$u2$都不会用到k+1短路及i短路$(i>k)$。
因为用k短路一定更短,用数学归纳法可以简单地证明。
所以我们只用处理前k短路,然后堆优化的dij每次弹出的恰好是里面目前最短的,也就是说每个点第i次被弹出,此时的dis就是第i小值。
然后可以发现每个点被弹出k次后就不用再用于更新其他点了。(性质$p1$)
每个点只会被用于处理k次,该算法的时间复杂度是$O(k \times( n \log n + m) )$
接下来考虑使用A*算法,即使用估价函数来优化bfs顺序。
由于A*的性质,所以估价函数$f(x)$应小于等于实际从起点走到终点的k短路在x到终点的部分。
所以我们选取估价函数$f(x)$表示x到终点的距离。
将dij中堆的优先级设为$\{h(x)=g(x)+f(x)\}_{min}$即可,其中$g(x)$表示从起点到目前状况的代价即可。
同样,每个点被弹出k次后就不用再用于更新其他点了。
正确性证明:
令$a(x)$表示实际总代价
$h(x)$表示总估价,
$g(x)$表示目前代价,
$f(x)$表示未来估价。
性质
$p1$:对于$u \to v , h(u)\leq h(v)$
文字理解:这一步做了最优决策,那么$h(u)$才能等于$h(v)$,其他选择都不优于最优选择,故$ h(u)\leq h(v)$
$p2$:对于拓扑序(不是拓扑序列) $h(x)$单调不减
由$p1$归纳得到。
$p3$(一句废话):对于同一个点,若$h(x_1)>h(x_2)$则$a(x_1)>a(x_2)$
即若按照堆的优先级来取,若第$i$短和第$i+1$短的都在堆里,那么每次取出一定是第$i$大。
由$p3$我们只需要证明当第i+1短的出堆时,i短的已经处理过了。
假设$i+1$由$k_2$转移来,$i$由$k_1$转移来。
即使$h(k_2)<h(k_1)$,那么$k_2$先被放入堆中,但是很明显有$h(k_1)\leq h(i) \leq h(i+1)$ 所以在$i+1$出堆前,$k_1$会进堆并在$i+1$出堆前扩展出$i$
使数学归纳法可推广至任意情况。
又因为$p3$同一个点的出堆顺序一定是正确的。
所以第$i$次取出的是第$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,谢谢你的阅读或转载!

浙公网安备 33010602011771号