hdu 6705

堆+贪心

超级钢琴的套路

先将每个点的出边按权值大小排序

维护一个四元组$(w, u, v, p)$,表示当前路径长度为$w$,当前边的起点是$u$,终点是$v$,这条边是$u$的出边中排序后的第$p$条边

每次出堆的路径加入答案,然后路径向外拓展,有两种情况,一种是加入$v$出边中最小的边,第二种是加入这条边的下一条边

然后输出答案即可

正确性:

对于这样的拓展方式,考虑错误的情况,如果存在一条路径比当前堆顶更优而没有加入堆中,这种情况不可能存在,因为这条路径可以由比他更优的路径拓展而来,如果这条路径没有被加入堆中,那么也就说明他的前继路径存在于堆中没有被选中,不合法,所以贪心策略正确。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 5;
int n, m, Q;
ll ans[maxn];
vector<pair<int, int> > G[maxn];
struct data {
    ll w;
    int u, v, p;
    data() {}
    data(ll _w, int _u, int _v, int _p) : w(_w), u(_u), v(_v), p(_p) {}
    bool friend operator < (const data &a, const data &b) {
        return a.w > b.w;
    }
};
int main() {
    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d%d%d", &n, &m, &Q);
        memset(ans, 0, sizeof(ans));
        for(int i = 1; i <= n; ++i) G[i].clear();
        priority_queue<data> q;
        for(int i = 1; i <= m; ++i) {
            int u, v, w; scanf("%d%d%d", &u, &v, &w);
            G[u].push_back(make_pair(w, v));
        } 
        for(int i = 1; i <= n; ++i) sort(G[i].begin(), G[i].end());
        for(int i = 1; i <= n; ++i) if(!G[i].empty()) q.push(data(G[i][0].first, i, G[i][0].second, 0));
        for(int _ = 1; _ <= 50000 && !q.empty(); ++_) {
            data t = q.top();
            q.pop();
            ans[_] = t.w;
            int u = t.u, v = t.v, p = t.p;
            if(G[v].size()) q.push(data(t.w + G[v][0].first, v, G[v][0].second, 0));
            if(p != (int)G[u].size() - 1) q.push(data(t.w + G[u][p + 1].first - G[u][p].first, u, G[u][p + 1].second, p + 1));
        }
        while(Q--) {
            int k; scanf("%d", &k);
            printf("%lld
", ans[k]);
        }
    }
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/19992147orz/p/11405833.html