题意:n个点,m条带权有向边,k次免费通行的机会,求最短路。
思路:有dp的方法和简单的分层最短路做法,分层的思路如下:把每个点都拆成k+1个分点,把图变成k+1个层次,每个层次的点除了连着同层的下一个点之外,还有一条免费边连着下一层对应的点。因为层数限制为k+1,也就是路径最多k次穿越不同层次,也就满足题目k次免费的情景。在这个图的基础上做一次最短路就能得到答案了,时间复杂度用dijkstra是OK的。
下面是一个简单的例子(k=2):
后来我发觉这个图其实还是有点粗糙, 严格地来说,1号位置只需要一个点就够了,这是因为我们希望走0边的机会尽量多。为了建图方便,所以代码里是都拆了一样多的点。然后最后答案也不用去额外连一个end节点去求,已经知道终点了,只要终点的每一层都遍历一次即可。
还有个很奇怪的地方是数据范围,似乎要比预期得还要大许多。
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1500005;
const int M=3000500;
const ll INF=0x7f7f7f7f;
int n,m,k,T;
int head[N],ent;
struct Edge{
int v,nx,w;
}ed[M<<1];
void AddEd(int u,int v,int w)
{
ed[++ent]={v,head[u],w};
head[u]=ent;
}
struct Qnode{
int n; ll d;
friend bool operator < (Qnode a,Qnode b)
{return a.d>b.d;}
};
ll dist[N];
void Dijkstra(int u)
{
priority_queue<Qnode> q;
dist[u]=0;
q.push({u,0});
while(!q.empty()){
Qnode p=q.top();
q.pop();
u=p.n;
for(int i=head[u],v;~i;i=ed[i].nx){
v=ed[i].v;
if(dist[v]>dist[u]+ed[i].w){
dist[v]=dist[u]+ed[i].w;
q.push({v,dist[v]});
}
}
}
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&k);
memset(head,-1,sizeof head);
memset(dist,INF,sizeof dist);
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
for(int j=0;j<=k;j++){
AddEd(u+j*n,v+j*n,w);
if(j<k)
AddEd(u+j*n,v+j*n+n,0);
}
}
Dijkstra(1);
ll ans=INF;
for(int i=0;i<=k;i++)
ans=min(ans,dist[n+n*k]);
printf("%lld\n",ans);
ent=0;
}
}