P2901 [USACO08MAR]Cow Jogging G
思路:先反向建边求各点到节点1的最短路,再A*依次得到节点(n)到节点(1)的第(k)短路。
总体来说就是改进版的BFS,在BFS的基础上另外设定了一个估价函数作为节点出队顺序的依据,而不像BFS那样先入队者先出队。在本题中,估价最小的节点会被优先拓展,然后是次小的、更小的……每次拓展到终点(1)时,都形成一条通路,越先形成的通路越短。
const int INF = 0x3f3f3f3f;
const int maxn = 1e4 + 100;
inline int read() {
int X = 0, w = 0; char ch = 0;
while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
return w ? -X : X;
}
int n, m, k;
int d[1100];
//各点到池塘的距离
bool done[1100];
int ahead[1100], anum = 0;
int head[1100], num = 0;
struct Edge {
int next, to, dis;
}ae[maxn],e[maxn];
struct Heapnode {
int u, dis;
bool operator < (const Heapnode& t) const {
return dis > t.dis;
}
Heapnode(int from, int d) :u(from), dis(d) {}
};
struct node {
int pos, dis;
//dis为 起点到pos的最短距离
bool operator <(const node& t)const {
return dis + d[pos] > t.dis + d[t.pos];
//加上点pos到终点的估计距离来比较,总估值最小的优先
}
node(int u, int d) :pos(u), dis(d) {}
};
void addedge1(int from, int to, int dis) {
anum++;
ae[anum].next = ahead[from];
ae[anum].to = to;
ae[anum].dis = dis;
ahead[from] = anum;
}
void addedge2(int from, int to, int dis) {
num++;
e[num].next = head[from];
e[num].to = to;
e[num].dis = dis;
head[from] = num;
}
void dij(int s) {
priority_queue<Heapnode> pq;
d[s] = 0;
pq.push(Heapnode(s,d[s]));
while (pq.size()) {
Heapnode tmp = pq.top(); pq.pop();
int u = tmp.u;
if (done[u]) continue;
for (int i = ahead[u]; i; i = ae[i].next) {
if (d[u] + ae[i].dis < d[ae[i].to]) {
d[ae[i].to] = d[u] + ae[i].dis;
pq.push(Heapnode(ae[i].to, d[ae[i].to]));
}
}
done[u] = true;
}
}
int Astar(int k) {
priority_queue<node> Q;
Q.push(node(n, 0));
while (Q.size()) {
int u = Q.top().pos;
int dis = Q.top().dis;
Q.pop();
//取到终点
if (u == 1) {
k--;
cout << dis << endl;
if (!k) return 0;
//前k短路已经全部求出
}
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to, w = e[i].dis;
Q.push(node(v, w + dis));
//w是点u到点v的距离
//dis是起点到点u的距离
//w+dis是起点到点v的距离
}
}
//要计算前k短路,但可能只能算出前a短路,剩下的不存在
//优先队列为空时能计算的前a短路已经算完
//剩下的k-a行只能输出-1
return k;
}
int main() {
n = read();m = read();k = read();
memset(d, INF, sizeof(d));
memset(done, false, sizeof(done));
for (int i = 1; i <= m; i++) {
int u, v, dis;
u = read(); v = read(); dis = read();
addedge1(v, u, dis);
//反向建边存图来求各点到终点的最短路
addedge2(u, v, dis);
//正常建边存图来Astar
//注意别存成了双向边
}
dij(1);
int last = Astar(k);
while (last--) cout << -1 << endl;
return 0;
}