启发式搜索 luogu2483——[SDOI2010]魔法猪学院

本文详细介绍了如何解决luogu2483问题,重点探讨了在求解k短路时采用Dijkstra、SPFA与A*搜索算法的结合。通过A*算法的f(x)=g(x)+h(x)核心思想,解释了估价函数h(x)的设计,并指出在该问题中,dijkstra或SPFA用于预计算T到所有节点的估价。代码实现中采用了大根堆,并强调了状态的独特性,避免了使用close表。
摘要由CSDN通过智能技术生成

luogu2483
这题不知道为什么,我bzoj上死都过不去
这里写图片描述
然而luogu不断的调数组大小后终于卡过去了(黑人问号
内存206MB。。时间4s+
本题重点就在于求出k短路
然后很简单的贪心减一下就好了

那么接下来问题来了 k短路怎么求

Dijkstra/SPFA+A*
前一个相信大家都知道并且会写
那么注重来讲一下本题中的A*要怎么操作

首先A*的核心在于:f(x)=g(x)+h(x)
那么这里的估价函数h(x)要怎么设计呢
很显然啊。。。x->T的最短路径
所以一开始的dij或者SPFA就是用来求出T到所有节点的估价函数
g(x)为节点走到当前状态的路径长度
注意一下 并不是最短路!
原地绕两下是允许的

接下来我们只要维护一个open表
这题。。连close表都不需要
因为状态本身并不会重复:{x,g(x)}

那么先看一下A*的代码

priority_queue<hp1> open;
inline void A_star()
{
    open.push((hp1){1,1,dis[1]});
    g[1]=0;
    while(!open.empty())
    {
        hp1 t=open.top();
        open.pop();
        if(t.x==n){if(e>=t.val) e-=t.val,ans++;else return;}
        for(int i=f[t.x];i;i=nxt[i])
        {

            int t1=++tot;
            int t2=poi[i];
            double t3=g[t1]=g[t.num]+v[i];
            //cout<<t1<<' '<<t2<<' '<<t3<<endl;
            open.push((hp1){t1,t2,t3+dis[t2]});
        }
    }
}

不要问我为什么是大根堆。。

 bool operator < (const hp1 &a) const
    {
        return a.val<val;
    }

然后加上一个dij就可以了
CODE:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#define N 200000
#define For(i,j,k) for(int i=j;i<=k;++i)
using namespace std;

double z,g[500001];
int n,m,x,y,s,cnt,ans,tott;
double c1[N*2],dis[20001],e,v[N*2];
int tot,nxt1[N*2],point1[20001],v1[N*2],poi[N*2],f[20001],nxt[N*2];

bool vis[N*2];
struct hp
{
    int x;
    double val;
    bool operator < (const hp &a) const
    {
        return a.val<val;
    }
};
struct hp1
{
    int num,x;
    double val;
    bool operator < (const hp1 &a) const
    {
        return a.val<val;
    }
}que[500001];
priority_queue <hp> q;

inline void addedge(int x,int y,double z)
{
    nxt1[++tot]=point1[x]; point1[x]=tot; v1[tot]=y; c1[tot]=z;
}
inline void add2(int x,int y,double z)
{
    nxt[++cnt]=f[x];f[x]=cnt;poi[cnt]=y;v[cnt]=z;
}
inline void dijkstra()
{
    For(i,1,n)  dis[i]=10000000.0;
    dis[n]=0;
    q.push((hp){n,0});
    while (!q.empty())
    {
        int now=q.top().x;
        q.pop();
        if (vis[now]) continue; 
        vis[now]=true;
        for (int i=point1[now];i;i=nxt1[i])
        {
            if (!vis[v1[i]]&&dis[now]+c1[i]<dis[v1[i]])
            {

                dis[v1[i]]=dis[now]+c1[i];
                q.push((hp){v1[i],dis[v1[i]]});
            }
        }
    }
}
priority_queue<hp1> open;
inline void A_star()
{
    open.push((hp1){1,1,dis[1]});
    g[1]=0;
    while(!open.empty())
    {
        hp1 t=open.top();
        open.pop();
        if(t.x==n){if(e>=t.val) e-=t.val,ans++;else return;}
        for(int i=f[t.x];i;i=nxt[i])
        {

            int t1=++tot;
            int t2=poi[i];
            double t3=g[t1]=g[t.num]+v[i];
            open.push((hp1){t1,t2,t3+dis[t2]});
        }
    }
}
int main()
{
    scanf("%d%d%lf",&n,&m,&e);
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d%lf",&x,&y,&z);
        addedge(y,x,z);
        add2(x,y,z);
    }
    dijkstra();
    A_star();
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值