A*
不会。
最短路径树+可并堆+优先队列
首先求所有点到的最短路,构建出最短路径树。
转化:一条到的路径可以双射为路径上的非树边序列
正确性: 每条路径唯一对应……(显然)
每个合法序列相邻非树边之间连接的树边顺序唯一,所以也唯一对应嘛。
而且路径长度是:加上所有非树边到贡献。证明很显然。
当然这个序列是有限制的。因为最短路径树的树边都是往上父亲指的。所以说相邻两个非树边,后面的开始点是前面的结尾点的祖先。
找前大,最短路可求大,每次找排名下一位的路径。
存在一种序列的构造方法:
1.修改:将最后一条边改成最小的不小于它的边。
2.添加:加一条尽量小的边,新边开始点满足:在结尾点的祖先的子树中。
用可并堆维护每个节点为起点的边(以边权为关键字从小到大),要查询所有祖先的信息,所以用可持久化可并堆每个节点存入所有祖先的信息,也就是在父亲的基础上加入自己的即可。
可以用优先队列模拟这两条。队列里维护当前路径长度,以及所在可并堆节点。
每次取出最优的,去构造其它的。
1.修改的边要和该边同样范围内,因此就是在该可并堆中第一个比它大的,也就是把左右儿子加入优先队列中。
2.查询当前边结束点的祖先信息,即把结束点可并堆的堆顶节点加入。
- code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef double db;
const int N = 1e6 + 5;
const int M = 6e6 + 5;
const db eps = 1e-9;
const db inf = 1000000000000000000;
int n, m, fa[N];
db E, dis[N];
struct edge {
int head[N], to[N], nxt[N], ecnt;
db len[N];
void add_edge(int u, int v, db w) {nxt[++ecnt] = head[u]; to[ecnt] = v; len[ecnt] = w; head[u] = ecnt;};
}Z, F;
int nd, rt[N];
struct Heap {
int ls, rs, d0, ed;
db w;
}H[M];
int _new(int ed, db w) {
++nd; H[nd].ed = ed; H[nd].w = w; return nd;}
int Merge(int u, int v) {
if(!u || !v) return u + v;
if(H[u].w > H[v].w) swap(u, v);
int nw = ++nd;
H[nw] = H[u];
H[nw].ls = Merge(H[nw].ls, v);
if(H[H[nw].ls].d0 > H[H[nw].rs].d0) swap(H[nw].ls, H[nw].rs);
H[nw].d0 = H[H[nw].ls].d0 + 1;
return nw;
}
struct pq {
int p; db w;
bool operator<(const pq &u) const{return w - u.w > eps;}
};
priority_queue<pq> Q;
queue<int> P;
bool In_s[N];
void spfa(int s) {
for(int i = 1; i <= n; i++) dis[i] = inf;
dis[s] = 0; P.push(s);
while(!P.empty()) {
int u = P.front(); P.pop();
In_s[u] = 0;
for(int i = F.head[u]; i; i = F.nxt[i]) {
int v = F.to[i];
if(dis[v] - (dis[u] + F.len[i]) > eps) {
dis[v] = dis[u] + F.len[i];
fa[v] = i;
if(!In_s[v]) In_s[v] = 1, P.push(v);
}
}
}
}
int pos[N];
void Build() { //最短路径树+左偏树维护挂在到 T 路径的子树内的边
for(int i = 1; i <= n; i++) {pos[i] = i;}
sort(pos + 1, pos + 1 + n, [&](int u, int v) {return dis[v] - dis[u] > eps;});
for(int i = 1; i <= n; i++) {
int u = pos[i], f = Z.to[fa[u]];
for(int j = Z.head[u]; j; j = Z.nxt[j]) {
int v = Z.to[j];
if(j == fa[u]) continue;
rt[u] = Merge(rt[u], _new(v, dis[v] + Z.len[j] - dis[u])); //非树边
}
rt[u] = Merge(rt[u], rt[f]);
}
}
void solve() {
int ans = 0;
if(dis[1] - E > eps) {puts("0"); return;}
E -= dis[1]; ans++;
if(rt[1]) Q.push((pq){rt[1], dis[1] + H[rt[1]].w});
while(!Q.empty()) {
int u = Q.top().p; db w = Q.top().w; Q.pop();
if(w - E > eps) {break;}
E -= w; ans++;
int ed = H[u].ed, ls = H[u].ls, rs = H[u].rs;
//change
if(ls) Q.push((pq){ls, w - H[u].w + H[ls].w});
if(rs) Q.push((pq){rs, w - H[u].w + H[rs].w});
//add
if(rt[ed]) Q.push((pq){rt[ed], w + H[rt[ed]].w});
}
printf("%d\n", ans);
}
int main() {
scanf("%d%d%lf", &n, &m, &E);
for(int i = 1; i <= m; i++) {
int u, v; db w; scanf("%d%d%lf", &u, &v, &w);
if(u == n) continue;
Z.add_edge(u, v, w); F.add_edge(v, u, w);
}
spfa(n);
Build();
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)