传送门:牛客
题目描述:
小明现在要追讨一笔债务,已知有n座城市,每个城市都有编号,城市与城市之间存在道路相连
(每条道路都是双向的),经过任意一条道路需要支付费用。小明一开始位于编号为1的城市,欠
债人位于编号为n的城市。小明每次从一个城市到达另一个城市需要耗时1天,而欠债人每天都会
挥霍一定的钱,等到第k天后(即第k+1天)他就会离开城n并再也找不到了。小明必须要在他离开
前抓到他(最开始为第0天),同时希望自己的行程花费和欠债人挥霍的钱的总和最小,你能帮他
计算一下最小总和吗?
3 3 2
1 3 10
1 2 2
2 3 2
3 7
输出:
13
一道有边数限制的最短路的题目
对于我们 k k k,我们发现最大范围才10,所以我们显然是可以通过枚举到最终点的边数的.所以问题最终就变成了计算经过k条边的最短路.
对于这个问题,最简单的方法是用 b e l l m a n _ f o r d bellman\_ford bellman_ford来解决,众所周知, b e l l m a n _ f o r d bellman\_ford bellman_ford来跑最短路的算法思想是跑每一条边的松弛,也就是枚举加入每一条边之后的最短路.那么对于我们的k来说,我们只需要将我们原本外层中的n改为k即可
下面是 b e l l m a n _ f o r d bellman\_ford bellman_ford的部分算法:
void bellman_ford() {
memset(dis[1],0x3f,sizeof(dis[1]));
dis[1][1]=0;
for(int i=1;i<=k;i++) {
memcpy(dis[0],dis[1],sizeof(dis[1]));
for(int j=1;j<=cnt;j++) {
if(dis[1][edge[j].v]>dis[0][edge[j].u]+edge[j].w) {
dis[1][edge[j].v]=dis[0][edge[j].u]+edge[j].w;
if(edge[j].v==n) ans=min(ans,dis[1][n]+sum[i]);
}
}
}
}
对于此题来说,我们的k比较小,此时我们的 b e l l m a n _ f o r d bellman\_ford bellman_ford跑的飞快,但是当我们的k很大的时候,此时这个算法就不行了,所以此时我们想到了什么,当然是 d i j k s t r a dijkstra dijkstra啦(此处指堆优化的 d i j k s t r a dijkstra dijkstra).
我们可以将我们原本的
d
i
s
[
u
]
dis[u]
dis[u]数组改为
d
i
s
[
d
a
y
]
[
u
]
dis[day][u]
dis[day][u],用来记录经过了
d
a
y
day
day条边到
u
u
u点的最短路.那么对于每一条
u
u
u的邻接点
v
v
v来说,我们将原本的松弛操作改一下就行:
d
i
s
[
d
a
y
+
1
]
[
v
]
=
d
i
s
[
d
a
y
]
[
u
]
+
e
d
g
e
[
u
]
[
i
]
.
d
i
s
t
+
c
o
s
t
[
d
a
y
]
dis[day+1][v]=dis[day][u]+edge[u][i].dist+cost[day]
dis[day+1][v]=dis[day][u]+edge[u][i].dist+cost[day]
接下来简单解释一下这么做的正确性:原本的 d i j k s t r a dijkstra dijkstra将到一个点的所有路取了一个 m a x max max,而此时我们只是将上述中的 m a x max max,拆出来而已,对于 d i j k s t r a dijkstra dijkstra的贪心想法显然是没有破坏的.只是对于我们后来到达该点的所有结果依旧保留而已.
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int n,m,k;int a[maxn];
struct Node{
int v,w;
};
vector<Node>edge[maxn];
struct heapnode{
int u,d,day;
bool operator <(const heapnode &rhs) const {
return d>rhs.d;
}
};
int vis[20][2000],dis[20][2000];
void dij(int S) {
priority_queue<heapnode>q;
memset(dis,0x3f,sizeof(dis));
dis[0][S]=0;
q.push({S,0,0});
while(!q.empty()) {
heapnode f=q.top();q.pop();
int u=f.u,day=f.day;
if(vis[day][u]) continue;
vis[day][u]=1;
if(day==k&&edge[u].size()) continue;
for(int i=0;i<edge[u].size();i++) {
int v=edge[u][i].v;
if(dis[day+1][v]>dis[day][u]+edge[u][i].w+a[day+1]) {
dis[day+1][v]=dis[day][u]+edge[u][i].w+a[day+1];
q.push({v,dis[day+1][v],day+1});
}
}
}
}
int main() {
n=read();m=read();k=read();
for(int i=1;i<=m;i++) {
int u=read(),v=read(),w=read();
edge[u].push_back({v,w});
edge[v].push_back({u,w});
}
for(int i=1;i<=k;i++) {
a[i]=read();
}
dij(1);
int ans=int_INF;
for(int i=1;i<=k;i++) {
ans=min(ans,dis[i][n]);
}
if(ans==int_INF) printf("-1\n");
else cout<<ans<<endl;
return 0;
}