USACO 改造路

题意

给定一个\(N\)个点的带权无向图,你可以选择\(K\)条边,把它们的边权改为\(0\)。请最小化\(1\)\(N\)的最短路

\(N,M\leq 5\times 10^4,K\leq 20\)


解法

原来分层图还有在dinic以外的应用,涨知识了

可以很容易得到一个DP式:设\(f[n][k]\)为到\(n\)号点,改变了\(k\)条边的边权的最短路长度
\[ \ \ f[to[x]][k]=min\{f[x][k]+val\} \\ f[to[x]][k]=min\{f[x][k-1]\} \]
发现\(k\)很小,可以直接在跑最短路时枚举\(k\)转移(每个点看做一个二元组)

也可以通过构造分层图的方式来解决,时空复杂度基本相同,且分层图更好理解

把原图复制\(k\)遍,其中,第\(i\)层与第\(i+1\)层之间连接权值为\(0\)的边。每向上走一层,相当于修改了一条边的边权

最后的答案即为\(dis[N\times K+N]\)

这个过程实际上与上面的DP是一样的


代码

#include 
#include 
#include  

using namespace std;

const int MAX_N = 4200000;

int N, M, K;

int cap;
int head[MAX_N], to[MAX_N], nxt[MAX_N], val[MAX_N];

int vis[MAX_N], dis[MAX_N];

struct node {
    int u, d;
    bool operator < (const node& rhs) const { return d > rhs.d; }
};

priority_queue que;

inline void add(int x, int y, int z) {
    to[++cap] = y, nxt[cap] = head[x], head[x] = cap, val[cap] = z;
}

void Dijkstra() {
    for (int i = 1; i <= MAX_N - 10; ++i) dis[i] = 1e9; 
    
    dis[1] = 0;
    que.push((node){1, 0});
    
    while (!que.empty()) {
        int x = que.top().u; que.pop();
        if (vis[x])  continue;
        vis[x] = 1;
        for (int i = head[x]; i; i = nxt[i]) {
            if (dis[x] + val[i] < dis[to[i]]) {
                dis[to[i]] = dis[x] + val[i];
                if (!vis[to[i]])
                    que.push((node){to[i], dis[to[i]]});
            }
        }
    }
    
}

int main() {
    
    scanf("%d%d%d", &N, &M, &K);
    
    int u, v, w;
    for (int i = 1; i <= M; ++i) {
        scanf("%d%d%d", &u, &v, &w);
        for (int j = 0; j <= K; ++j) {
            add(j * N + u, j * N + v, w);
            add(j * N + v, j * N + u, w);
            if (j ^ K)  
                add(j * N + u, (j + 1) * N + v, 0),
                add(j * N + v, (j + 1) * N + u, 0);                     
        }
    }
    
    Dijkstra();
    
    printf("%d\n", dis[(K + 1) * N]);
    
    return 0;
}

你可能感兴趣的:(USACO 改造路)