刷题记录:牛客NC14700追债之旅

文章描述了一种针对有限边数限制的最短路径问题,小明需在欠债人逃离前从城市1到达城市n,花费和欠债人挥霍的总和要最小。解决方案包括使用bellman_ford算法在k较小的情况下,以及当k较大时采用堆优化的dijkstra算法。
摘要由CSDN通过智能技术生成

传送门:牛客

题目描述:

小明现在要追讨一笔债务,已知有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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值