1. 程式人生 > 實用技巧 >[毒瘤][A*][SDOI2010] 魔法豬學院

[毒瘤][A*][SDOI2010] 魔法豬學院

先把程式碼放這, 解釋下次再說

其實讓我很不解明明是個 \(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;
        }

        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 為預估

// 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;
}