POJ 2449 K短路(A*)

11 篇文章 0 订阅
POJ 2449
复杂度O(nk)
注意图不连通 起始点终点为同一点的情况
#include <cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const LL maxn = 1000 + 100;
const LL maxm = 200000 + 100;
const LL inf = 0x3f3f3f3f3f3f3f3f;
LL he2[maxn],ne2[maxm],ver2[maxm],tot2,cost2[maxm];
LL dist[maxn],vis[maxn];
void add2( LL x,LL y,LL c ){
    ver2[++tot2] = y;
    ne2[tot2] = he2[x];
    he2[x] = tot2;
    cost2[tot2] = c;
}
priority_queue< pair<LL,LL>,vector< pair<LL,LL> >,greater< pair<LL,LL> > > que2;
void dijkstra( LL S,LL T ){
    while( que2.size() ) que2.pop();
    dist[S] = 0;
    que2.push( make_pair( 0,S ) );
    while( que2.size() ){
        LL x = que2.top().second;
        que2.pop();
        //if( x == T ) break;
        if( vis[x] ) continue;
        vis[x] = 1;
        for( LL cure = he2[x];cure;cure = ne2[cure] ){
            LL y = ver2[cure];
            if( vis[y] ) continue;
            if( dist[y] > dist[x] + cost2[cure] ){
                dist[y] = dist[x] + cost2[cure];
                que2.push( make_pair( dist[y],y ) );
            }
        }
    }
}
LL he[maxn],ne[maxm],ver[maxm],tot,cost[maxm];
void add( LL x,LL y,LL c ){
    ver[++tot] = y;
    ne[tot] = he[x];
    he[x] = tot;
    cost[tot] = c;
}
priority_queue< pair<LL, pair<LL,LL> > ,vector< pair< LL,pair<LL,LL> > >,greater< pair< LL,pair<LL,LL> > > > que;
LL solve( LL S,LL T,LL k ){
    while( que.size() ) que.pop();
    que.push( make_pair( dist[S], make_pair( 0 , S )  ) );
    LL ans = 0;
    while( que.size() ){
        LL d = que.top().second.first;
        LL x = que.top().second.second;
        if( x == T ){
            ans++;
            if( ans == k ) return que.top().first;
        }
        que.pop();
        for( LL cure = he[x];cure;cure = ne[cure] ){
            LL y = ver[cure];
            que.push( make_pair( d+ cost[cure] + dist[ y ],make_pair( d + cost[cure] ,y ) ) );
        }
    }
    return -1;
}
void init( LL n ){
    for( LL i = 1; i <= n;i++ ){
        he[i] = he2[i] = vis[i] = 0;
        dist[i] = inf;
    }
    tot = tot2 = 1;
}
int main()
{
    LL n,m,a,b,t,S,T,k;
    while( 2 == scanf("%lld%lld",&n,&m) ){
        init( n );
        for( LL i = 1;i <= m;i++ ){
            scanf("%lld%lld%lld",&a,&b,&t);
            add( a,b,t );
            add2( b,a,t );
        }
        scanf("%lld%lld%lld",&S,&T,&k);
        if( S == T ) k++;
        dijkstra( T,S );
        if( dist[S] == inf ){
            printf("-1\n");
            continue;
        }
        LL ans = solve( S,T,k );
        printf("%lld\n",ans);
    }
    return 0;
}

下面写一下对于A*算法的个人理解,大佬请移步

记 起始点 S,终点T。S走到当前节点 i 的实际代价 g( i ),S走到 i 最小代价 g*(i),i 走到 T 的最小代价h*( i )。i走到T的估价 h(i)

记c(i,j)为节点 i 到 j 的最短路径

1.保证第一次扩展 T 时即得到最短路得条件:h( n )  < h*( n )

证明:易知最短路径上的某一点一定会在优先队列中,设此节点为  i  且此节点的权值是 f( i ) = g( i ) + h( i ) ,又 i为最短路径中的点,则 g(i) = g*( i )。

下面用反证法

假设节点 i 在被取出前 T已经被取出,则 f( T )  <  f( i )  => g( T )  <  g( i ) + h( i )  => g( T )  <  g*( i ) + h( i ) => g( T ) < g*( T )

显然不成立。

2.保证第一次扩展任意一节点  k 时即得到 S到 k 的最短路径的条件  c(i , j)> h( j )  - h( i )

证明:可以将 h( j ) - h( i )  理解为 i 到 j 的估价函数。则证明同上

 

于是易知 A* k短路算法的正确性,T第k次出队列的 时即为 第k短路。

同时还可以得到一个小结论:

dijsktra 可理解为估价值为0的A* 则其同时满则条件1和条件2,于是每个节点第一次扩展时即可得到到该点的最短路。

第k次扩展时便可得到起点到该点的第k短路。同样的结论应用于该题的A*算法中时只对于 终点 T成立,原因请读者自行思考。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一道比较经典的计数问题。题目描述如下: 给定一个 $n \times n$ 的网格图,其中一些格子被标记为障碍。一个连通块是指一些被标记为障碍的格子的集合,满足这些格子在网格图中连通。一个格子是连通的当且仅当它与另一个被标记为障碍的格子在网格图中有公共边。 现在,你需要计算在这个网格图中,有多少个不同的连通块,满足这个连通块的大小(即包含的格子数)恰好为 $k$。 这是一道比较经典的计数问题,一般可以通过计算生成函数的方法来解决。具体来说,我们可以定义一个生成函数 $F(x)$,其中 $[x^k]F(x)$ 表示大小为 $k$ 的连通块的个数。那么,我们可以考虑如何计算这个生成函数。 对于一个大小为 $k$ 的连通块,我们可以考虑它的形状。具体来说,我们可以考虑以该连通块的最左边、最上边的格子为起点,从上到下、从左到右遍历该连通块,把每个格子在该连通块中的相对位置记录下来。由于该连通块的大小为 $k$,因此这些相对位置一定是 $(x,y) \in [0,n-1]^2$ 中的 $k$ 个不同点。 现在,我们需要考虑如何计算这些点对应的连通块是否合法。具体来说,我们可以考虑从左到右、从上到下依次处理这些点,对于每个点 $(x,y)$,我们需要考虑它是否能够与左边的点和上边的点连通。具体来说,如果 $(x-1,y)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们就是连通的;同样,如果 $(x,y-1)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们也是连通的。如果 $(x,y)$ 与左边和上边的点都不连通,那么说明这个点不属于该连通块。 考虑到每个点最多只有两个方向需要检查,因此时间复杂度为 $O(n^2 k)$。不过,我们可以使用类似于矩阵乘法的思想,将这个过程优化到 $O(k^3)$ 的时间复杂度。 具体来说,我们可以设 $f_{i,j,k}$ 表示状态 $(i,j)$ 所代表的点在连通块中,且连通块的大小为 $k$ 的方案数。显然,对于一个合法的 $(i,j,k)$,我们可以考虑 $(i-1,j,k-1)$ 和 $(i,j-1,k-1)$ 这两个状态,然后把点 $(i,j)$ 加入到它们所代表的连通块中。因此,我们可以设计一个 $O(k^3)$ 的 DP 状态转移,计算 $f_{i,j,k}$。 具体来说,我们可以考虑枚举连通块所包含的最右边和最下边的格子的坐标 $(x,y)$,然后计算 $f_{x,y,k}$。对于一个合法的 $(x,y,k)$,我们可以考虑将 $(x,y)$ 所代表的点加入到 $(x-1,y,k-1)$ 和 $(x,y-1,k-1)$ 所代表的连通块中。不过,这里需要注意一个细节:如果 $(x-1,y)$ 和 $(x,y)$ 在网格图中没有相邻边,那么它们不能算作连通的。因此,我们需要特判这个情况。 最终,$f_{n,n,k}$ 就是大小为 $k$ 的连通块的个数,时间复杂度为 $O(n^2 k + k^3)$。 参考代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值