问题:给定一张有n个点,m条有向边的图。一个整数k。(n<=1e5,m<=2e5,k<=10)。你有k次机会使得图中的某一条边权值变为0。求1号点到n号点的最短距离。保证至少存在一条路径从1到n。

思路:dijkstra算法+dp转移

  这其实就是一道分层最短路的模板题。普通的dijkstra算法,dis数组记录的是起点到该点的最短距离,这里的话多开一维就好了。dis[i][j],代表起点到i点并且使用了j次机会的最短距离。转移时候分情况使用或不使用这次机会讨论来进行转移。因为k不超过10,所以空间复杂度和时间复杂度都是合理的。然后其他操作就和普通的dijkstra算法是一样的。具体见代码。

AC代码:

//#include<bits/stdc++.h>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ll, int> pli;
typedef pair<int, ll> pil;
#define pb push_back
#define X first
#define Y second
inline ll gcd(ll a, ll b) { while (b != 0) { ll c = a % b; a = b; b = c; }return a < 0 ? -a : a; }
inline ll lcm(ll a, ll b) { return (a * b) / gcd(a, b); }
inline ll lowbit(ll x) { return x & (-x); }
const double PI = 3.14159265358979323846;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
inline char nc() {
    static char buf[1 << 21], * p1 = buf, * p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline ll rd() {
    //#define getchar nc
    ll x = 0, f = 1; char ch = getchar();
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); }
    return x * f;
}
const double eps = 1e-6;
const int M = 1e6 + 10;
const int N = 1e5 + 10;
struct Edge {
    int to, next;
    ll w;
}e[N << 1];
int head[N];
int tot;
int n, m, k;
inline void add(int x, int y, ll w) {
    e[++tot] = { y,head[x],w };
    head[x] = tot;
}
ll dis[N][13];
bool vis[N][13];
struct MM {
    int node, use;
    ll val;
    bool friend operator<(const MM& m1, const MM& m2) {
        return m1.val > m2.val;
    }
};
priority_queue<MM>q;
inline ll dijkstra() {
    q.push({ 1,0 ,0 });
    dis[1][0] = 0;
    while (q.size()) {
        int x = q.top().node, use = q.top().use; q.pop();
        if (x == n) return dis[x][use];
        if (vis[x][use])continue;
        vis[x][use] = 1;
        for (int i = head[x]; i != -1; i = e[i].next) {
            int y = e[i].to;
            if (dis[y][use] > dis[x][use] + e[i].w) {//这一次不白嫖
                dis[y][use] = dis[x][use] + e[i].w;
                q.push({ y,use,dis[y][use] });
            }
            if (use < k && dis[y][use + 1] > dis[x][use]) {//这次白嫖
                dis[y][use + 1] = dis[x][use];
                q.push({ y,use + 1,dis[y][use + 1] });
            }
        }
    }
}
void init() {
    tot = 0;
    memset(head, -1, sizeof(head[0]) * (n + 5));
    memset(dis, INF, sizeof(dis[0]) * (n + 5));
    memset(vis, 0, sizeof(vis[0]) * (n + 5));
    while (q.size())q.pop();
}
int main() {
    int t = rd();
    while (t--) {
        n = rd(), m = rd(), k = rd();
        init();
        while (m--) {
            int u = rd(), v = rd();
            ll w = rd();
            add(u, v, w);
        }
        printf("%lld\n", dijkstra());
    }
}