题目大意:一个有N个点m条边的无向图,可以将路径上k条边的权值变为0,试求此时1-n的最短路径长度。
Sol:我们考虑分层图思想,将图复制k+1份,分别是0~k层,表示此时已经经过的“免费边”条数。
层与层之间的边权值为0且为单向由上层指向下层。
这样我们以0层的1点做单源最短路径。每一层的n点的距离最小值即为答案。
只不过这样的点数为O(K*N),边数为O(K*M),比较慢。
我的做法是,对每一层使用heap-dijkstra算法由本层的原点更新这一层的最短路长度。然后显然可以用O(m)的复杂度推知下一层的初始最短路长度。
这种做法显然空间和时间上均存在较大优势。
Code:
#include <cstdio> #include <cstring> #include <cctype> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; inline int getc() { static const int L = 1 << 15; static char buf[L], *S = buf, *T = buf; if (S == T) { T = (S = buf) + fread(buf, 1, L, stdin); if (S == T) return EOF; } return *S++; } inline int getint() { int c; while(!isdigit(c = getc())); int tmp = c - '0'; while(isdigit(c = getc())) tmp = (tmp << 1) + (tmp << 3) + c - '0'; return tmp; } typedef long long LL; #define N 10010 #define M 50010 int n, m, k; int head[N], next[M << 1], end[M << 1], len[M << 1]; LL dis[2][N]; bool inpath[N]; queue<int> q; void addedge(int a, int b, int _len) { static int q = 1; len[q] = _len; end[q] = b; next[q] = head[a]; head[a] = q++; } void make(int a, int b, int _len) { addedge(a, b, _len); addedge(b, a, _len); } struct Node { int lab, dis; Node(int _lab = 0, int _dis = 0):lab(_lab),dis(_dis){} bool operator < (const Node &B) const { return (dis < B.dis) || (dis == B.dis && lab < B.lab); } }; struct Heap { Node a[N]; int top, ch[N]; Heap():top(0){} void up(int x) { for(; x != 1; x >>= 1) { if (a[x] < a[x >> 1]) { swap(ch[a[x].lab], ch[a[x >> 1].lab]); swap(a[x], a[x >> 1]); } else break; } } void down(int x) { int son; for(; x << 1 <= top; ) { son=(((x<<1)==top)||(a[x<<1]<a[(x<<1)|1]))?(x<<1):((x<<1)|1); if (a[son] < a[x]) { swap(ch[a[son].lab], ch[a[x].lab]); swap(a[son], a[x]); x = son; } else break; } } void insert(Node x) { a[++top] = x; ch[x.lab] = top; up(top); } Node Min() { return a[1]; } void pop() { a[1] = a[top]; ch[a[top--].lab] = 1; down(1); } void change(int x, int to) { int ins = ch[x]; a[ins].dis = to; up(ins); } }H; void Dijkstra(bool d) { H.top = 0; int i, j; memset(inpath, 0, sizeof(inpath)); for(i = 1; i <= n; ++i) H.insert(Node(i, dis[d][i])); for(i = 1; i <= n; ++i) { Node tmp = H.Min(); H.pop(); inpath[tmp.lab] = 1; for(j = head[tmp.lab]; j; j = next[j]) { if (!inpath[end[j]] && dis[d][end[j]] > dis[d][tmp.lab] + len[j]) { dis[d][end[j]] = dis[d][tmp.lab] + len[j]; H.change(end[j], dis[d][end[j]]); } } } } int main() { n = getint(); m = getint(); k = getint(); int i, j; int a, b, x; for(i = 1; i <= m; ++i) { a = getint(); b = getint(); x = getint(); make(a, b, x); } int now = 0, last = 1; memset(dis, 0x3f, sizeof(dis)); dis[now][1] = 0; Dijkstra(now); LL ans = dis[now][n]; while(k--) { now ^= 1; last ^= 1; for(i = 1; i <= n; ++i) dis[now][i] = dis[last][i]; for(i = 1; i <= n; ++i) for(j = head[i]; j; j = next[j]) dis[now][end[j]] = min(dis[now][end[j]], dis[last][i]); Dijkstra(now); if (ans == dis[now][n]) break; ans = dis[now][n]; } printf("%lld", ans); return 0; }