[毒瘤][A*][SDOI2010] 魔法猪学院

先把代码放这, 解释下次再说

其实让我很不解明明是个 (A^*) 板子为什么还要卡裸 (A^*).. 给我这个只想先打个裸 (A^*) 看看自己理解的对不对 就是懒 的菜鸡带来了极大不便... 然后就直接特判了
其实一想也算是个提醒吧: 裸 (A^*) 的确很费空间.

注: 不特判打正解需要可持久化可并堆


代码:

# include <iostream>
# include <cstring>
# include <cstdio>
# include <queue>
# define MAXM 2000005
# define MAXN 5005

using namespace std;

int n, m;
double ener;

// Edge Section

struct edge{
    int u, v, next;
    double w;
    bool forw; // forw = 1 为正图上的边
}e[MAXM<<1];
int hd[MAXM<<1], cntE;

void AddE(int u, int v, double w){
    e[++cntE].u = u, e[cntE].v = v, e[cntE].w = w, e[cntE].forw = 1, e[cntE].next = hd[u], hd[u] = cntE;
    e[++cntE].u = v, e[cntE].v = u, e[cntE].w = w, e[cntE].forw = 0, e[cntE].next = hd[v], hd[v] = cntE;
} // 同时新建正反图的边

// Dij Section

struct node{
    int id;
    double val;
    bool operator < (const node that) const{
        return this->val < that.val;
    }
    bool operator > (const node that) const{
        return this->val > that.val;
    }
};

priority_queue<node, vector<node>, greater<node> >qDij;
double h[MAXN];
bool vis[MAXN];

void Dij(int from){
    for(int i = 1; i <= n; i++){
        h[i] = 1e9, vis[i] = 0;
    }
    h[from] = 0;
    qDij.push((node){from, 0});
    while(!qDij.empty()){
        node tmp = qDij.top();
        qDij.pop();

        if(vis[tmp.id]){
            continue;
        }

        vis[tmp.id] = 1;

        for(int i = hd[tmp.id]; i; i = e[i].next){
            if(!e[i].forw){
                if(h[tmp.id] + e[i].w < h[e[i].v]){
                    h[e[i].v] = h[tmp.id] + e[i].w;
                    qDij.push((node){e[i].v, h[e[i].v]});
                }
            }
        }
    }

} // 在反图上跑迪杰, 从终点开始

// A* Section

struct star{
    int id;
    double g, f;
    bool operator < (const star that) const{
        return this->g+this->f < that.g+that.f;
    }
    bool operator > (const star that) const{
        return this->g+this->f > that.g+that.f;
    }
};

priority_queue<star, vector<star>, greater<star> >qAs;

int cnt[MAXN]; // 记录某个点到了几次用于及时跳出
int ans = 0 ;
void Astar(int lim){
    if(h[1] > 1e9-5) return;

    star tmp, nxt;
    tmp.id = 1, tmp.g=0, tmp.f = 0;
    
    qAs.push(tmp);
    
    while(!qAs.empty()){
        tmp = qAs.top(); qAs.pop();

        if(tmp.g > ener) return; // 特判是否到达最大取值

        cnt[tmp.id]++;

        if(cnt[tmp.id] > lim) continue; // 特判优化

        if(tmp.id == n){
            ener -= tmp.g;
            ans++;
            continue;
        } // 每次搜到终点的答案都可以记录, 第 i 次就是第 i 短路
        // 相对于第 i 次搜索来说, 就是最短的
        // 因为更短的方法已经被弹出去了

        for(int i = hd[tmp.id]; i; i = e[i].next){
            if(e[i].forw){
                nxt.id = e[i].v;
                nxt.f = h[nxt.id];
                nxt.g = tmp.g + e[i].w;
                qAs.push(nxt);
            } // 延展到的点压入堆, 关键字为 g+h
        }
    }
} // g 为实际, h 为预估

// Main Function

int main(){
    scanf("%d%d%lf", &n, &m, &ener);
    
    int ua, va; double wa;
    
    for(int i = 1; i <= m; i++){
        scanf("%d%d%lf", &ua, &va, &wa);
        AddE(ua, va, wa);
    }

    Dij(n);

    if(ener/h[1]>1000000){
        cout<<2002000;
        return 0;
    } // 特判hack数据

    Astar(ener/h[1]); // 最大方案数不能超过 总能量/最短路

    printf("%d", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/Foggy-Forest/p/13345740.html