题目链接 http://acm.sdut.edu.cn/onlinejudge3/contests/4098/problems/M
简要题意:
给出一张 N个节点 M 条边的无向图,节点编号从 1到 N,每条边有一个正整数权重。
请你给出一条从 11 节点到 NN 节点的路径,使得边权值累加最小。此外你可以选择 K 条边,使其权重缩小为原来的 1/3
(向下取整)
解析:
• 对于这种花里胡哨的最短路题目,只要想好怎么建图剩下的就非常简单了。
• 我们考虑建图的时候每个节点,并不只是图上的一个节点,而是一个状态,每条边是不同状态间的
转移。
• 对于本题,我们定义状态 disti,j 为从 1 点出发到达 i 点并还可以减少 j 条边的花费时的最小费用,
比如假设有一个 u 到 v,花费为 w 的边,我们可以建 2K − 1 条边:
通过优先队列来决定接下来的方向,每一条边决定是不是要使用加速(通过比较耗费)。u是起始点,v是目标点。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX = 112;
const int INF = 0x3f3f3f3f;
vector<pair<int, int> > edge[MAX];
int dist[MAX][MAX];
int vis[MAX][MAX];
int N, M, K;
void init() {
memset(vis, 0, sizeof(vis));
for (int i = 0; i < MAX; ++i) edge[i].clear();
}
void dijkstra() {
memset(dist, INF, sizeof(dist));
priority_queue<pair<int, pair<int, int> > > q;
dist[1][K] = 0;
vis[1][K] = 1;
int sumx=0;
q.push(make_pair(1, make_pair(1, K)));//初始是k次,
while (!q.empty()) {
sumx++;
int u = q.top().second.first;
cout<<sumx<<" :"<<q.top().first<<endl;
int k = q.top().second.second;
q.pop();
for (int i = 0; i < edge[u].size(); i++) {
int v = edge[u][i].first;
int w = edge[u][i].second;
if (dist[v][k] > dist[u][k] + w) {
dist[v][k] = dist[u][k] + w;
if (!vis[v][k]) {
q.push(make_pair(-dist[v][k], make_pair(v, k)));
}
vis[v][k] = 1;
}
if (k > 0) {
w /= 3;
if (dist[v][k - 1] > dist[u][k] + w) {
dist[v][k - 1] = dist[u][k] + w;
if (!vis[v][k - 1]) {
q.push(make_pair(-dist[v][k - 1], make_pair(v, k - 1)));
}
vis[v][k - 1] = 1;
}
}
}
vis[u][k] = 0;
}
}
int main() {
int T;
cin >> T;
while (T--) {
init();
cin >> N >> M >> K;
for (int i = 0; i < M; ++i) {
int u, v, w;
cin >> u >> v >> w;
edge[u].emplace_back(v, w);
edge[v].emplace_back(u, w);
}
dijkstra();
int ans = INF;
for (int i = 0; i <= K; ++i) {
ans = min(ans, dist[N][i]);
}
if(ans == INF) {
cout << -1 <<endl;
} else {
cout << ans << endl;
}
}
}