题目链接:https://nanti.jisuanke.com/t/31001
题意:
一带权有向图,有 n 个节点编号1~n,m条有向边,现在一人从节点 1 出发,他有最多 k 次机会施展魔法使得某一条边的权变成 0,问他走到节点 n 的最小权值为多少。
题解:
将dist数组和vis数组都扩展一维:
dist[c][i]代表:已经使用了 c 次变0魔法后,走到节点 i 的最短距离;
vis[c][i]代表:已经使用了 c 次变0魔法后,走到节点 i 的最短距离,这个最短距离是否已经被准确计算完毕。
然后就是稍微改动一下原来的优先队列优化Dijkstra即可。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; const int maxm=2e5+10; const int maxk=13; const ll INF=0x3f3f3f3f3f3f3f3f; int n,m,k; struct Edge{ int u,v; ll w; Edge(int u=0,int v=0,ll w=0){this->u=u,this->v=v,this->w=w;} }; vector<Edge> E; vector<int> G[maxn]; void init(int l,int r) { E.clear(); for(int i=l;i<=r;i++) G[i].clear(); } void addedge(int u,int v,ll w) { E.push_back(Edge(u,v,w)); G[u].push_back(E.size()-1); } struct Qnode{ int vertex; ll dist; int cnt; Qnode(int v=0,ll d=0,int c=0){this->vertex=v,this->dist=d,this->cnt=c;} bool operator <(const Qnode &oth)const{ return dist>oth.dist; } //优先队列默认是降序排列,如果本节点小于另一个节点的意义是 本节点dist < 另一个节点dist, //则优先队列的队首放的就是最大的节点,就是dist最大的节点,显然我们要的是相反的情况, //所以设置本节点小于另一个节点的意义是 本节点dist > 另一个节点dist 即可。 }; ll dist[maxk][maxn]; bool vis[maxk][maxn]; void dijkstra(int s) { for(int i=1;i<=n;i++) { for(int c=0;c<=k;c++) { dist[c][i]=((i==s)?0:INF); vis[c][i]=0; } } priority_queue<Qnode> Q; Q.push(Qnode(s,0,0)); while(!Q.empty()) { int u=Q.top().vertex, c=Q.top().cnt; Q.pop(); if(vis[c][u]) continue; vis[c][u]=1; for(int i=0;i<G[u].size();i++) { Edge &e=E[G[u][i]]; int v=e.v; if(!vis[c][v] && dist[c][v]>dist[c][u]+e.w) { dist[c][v]=dist[c][u]+e.w; Q.push(Qnode(v,dist[c][v],c)); } if(c+1<=k && !vis[c+1][v] && dist[c+1][v]>dist[c][u]) { dist[c+1][v]=dist[c][u]; Q.push(Qnode(v,dist[c+1][v],c+1)); } } } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); init(1,n); for(int i=1;i<=m;i++) { int u,v; ll w; scanf("%d%d%lld",&u,&v,&w); addedge(u,v,w); } dijkstra(1); ll ans=INF; for(int c=0;c<=k;c++) ans=min(ans,dist[c][n]); printf("%lld ",ans); } }