WC冬眠完了, 寒假过完了(纳尼?居然过完了?!), 又回来继续学hun习dian;
k短路裸题嘛, 具体就是用一个优先队列来不停的更新到每个点的距离, 这样第k次出堆时得到的解就是到该点的k短路。
实际上我们用到中间变量来更新答案。 首先求出所有点到终点t的距离, 可以通过建立反边后跑最短路来求。 然后对于每个点x, 有一个变量w代表从s到x点的当前距离, 也就是说x每次出堆后再进来时w值都会最小程度地变大, 这样就达到了更新答案的目的。 更新的方程可表示为{x, w[x]} => {k, w[x] + d[x][k]}。 需要注意的是, 若一个点已经出过k次堆, 那么它对于解的后续贡献是没有了的, 这是应该果断舍弃。
**CAUTION**: 若s和t一样, 需将k的值加1。 我就在这里跪了一次...
#include <cstdio>
#include <algorithm>
#include <queue>
#define N 1000 + 10
#define M 200000 + 10
#define INF 1000000000
using namespace std;
struct edge
{
int to, w, next;
}e[M];
int n, m, num, s, t, kth, p[N], flag[N], d[N], cont[N];
struct node
{
int o, w;
node() { }
node(int x, int y)
{ o = x, w = y; }
bool operator < (const node &a) const
{ return w + d[o] > a.w + d[a.o]; }
};
void read(int &x)
{
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
x = 10*x + c - '0';
c = getchar();
}
}
void add(int x, int y, int z)
{
e[++num].to = y;
e[num].w = z;
e[num].next = p[x];
p[x]= num;
}
void init()
{
int x, y, z;
read(n), read(m);
for (int i = 1; i <= m; ++i)
{
read(x), read(y), read(z);
add(x, y, z);
add(y, x, z);
}
read(s), read(t), read(kth);
if (s == t) ++kth;
}
void spfa()
{
queue<int>q;
q.push(t);
for (int i = 1; i <= n; ++i)
d[i] = INF;
d[t] = 0;
flag[t] = 1;
while(!q.empty())
{
int x = q.front();
q.pop();
flag[x] = 0;
for (int i = p[x]; i; i = e[i].next)
{
if (i & 1) continue;
int k = e[i].to;
if (d[k] > d[x] + e[i].w)
{
d[k] = d[x] + e[i].w;
if (!flag[k])
{
flag[k] = 1;
q.push(k);
}
}
}
}
}
int Astar()
{
priority_queue<node>q;
q.push(node(s, 0));
node cur;
while(!q.empty())
{
cur = q.top();
q.pop();
int x = cur.o, w = cur.w;
++cont[x];
if (x == t && cont[x] == kth) return w;
if (cont[x] > kth) continue;
for (int i = p[x]; i; i = e[i].next)
if (i & 1)
{
int k = e[i].to;
q.push(node(k, w + e[i].w));
}
}
return -1;
}
void deal()
{
spfa();
printf("%d\n", Astar());
}
int main()
{
init();
deal();
return 0;
}