AtCoder Beginner Contest 164 E Two Currencies Bellman-Ford优化思想+01背包+动归dp状态转移

AtCoder Beginner Contest 164   比赛人数11302  比赛开始后15分钟看到所有题

AtCoder Beginner Contest 164  E  Two Currencies   Bellman-Ford优化思想+01背包+动归dp状态转移

总目录详见https://blog.csdn.net/mrcrack/article/details/104454762

在线测评地址https://atcoder.jp/contests/abc164/tasks/abc164_e

考察最短路,但换钱是个难题,会改变最短路径,在何处换钱,请注意,换钱很可能会改变路径走向。

dp[i][j]=k表示从节点1出发到达节点i,此时手头有j块银币,对应的最小消耗时间k,

三种状态(节点,银币数量,消耗时间),就用一个二维数组dp[][]给表示了.
Bellman-Ford算法分三种:Bellman-Ford算法,Bellman-Ford优化,Bellman-Ford队列优化(即SPFA算法)

因该题状态较多,若采用SPFA算法,可能会造成预先开的队列数组空间过大,超出内存限制,

故退一步,采用了Bellman-Ford优化。

看懂下面代码,读者需具备最短路算法,01背包算法。

基本思想:先有钱,再旅行,之后计算一无所有(手头0块银币)的最小消耗时间.

#include <cstdio>
#include <algorithm>
#define LL long long
#define M 3000
#define INF 1e15
//1e15由来:换钱耗时+旅行耗时=(D*A*N)+(B*N)50*=10^9*50*50+10^9*50
using namespace std;
//3010由来,旅行最多经过N个点(N-1条边),每条边最多花费50,故总花费50*50=2500,再大些,取3010
LL dp[55][3010];//换钱耗时+旅行耗时=(D*A*N)+(B*N)50*=10^9*50*50+10^9*50很明显int不够用
int c[55],d[55],head[55],tot;
struct node{
	int to,next,a,b;
}e[105*2];//无向图
void add_edge(int u,int v,int a,int b){//邻接表
	tot++,e[tot].to=v,e[tot].a=a,e[tot].b=b,e[tot].next=head[u],head[u]=tot;
}
int main(){
	int n,m,s,u,v,a,b,i,j,flag;
	scanf("%d%d%d",&n,&m,&s);
	for(i=1;i<=m;i++){
		scanf("%d%d%d%d",&u,&v,&a,&b);
		add_edge(u,v,a,b),add_edge(v,u,a,b);//无向图
	}
	for(i=1;i<=n;i++)scanf("%d%d",&c[i],&d[i]);
	for(i=1;i<=n;i++)
		for(j=0;j<=M;j++)//注意j从0开始,因最终目标是求dp[i][0],故j从0开始
			dp[i][j]=INF;
	s=min(s,M);//银币太多,无用
	dp[1][s]=0;//初始位置情况
	while(1){
		flag=0;
		for(u=1;u<=n;u++){//u所处节点
			for(j=0;j<=M;j++)//j手上银币数量
				if(j>=c[u]&&dp[u][j]>dp[u][j-c[u]]+d[u]) dp[u][j]=dp[u][j-c[u]]+d[u];//先换钱,01背包,j-c[u]能装进c[u]变成j
			for(j=0;j<=M;j++)
				for(b=head[u];b;b=e[b].next){//再旅行
					v=e[b].to;
					if(j>=e[b].a&&dp[v][j-e[b].a]>dp[u][j]+e[b].b)//j消耗掉e[b].a,才能到j-e[b].a状态 v->u->1
						flag=1,dp[v][j-e[b].a]=dp[u][j]+e[b].b;//动归dp,状态转移
				}
			for(j=M-1;j>=0;j--)dp[u][j]=min(dp[u][j],dp[u][j+1]);//清空手头的银币,但不消耗时间
		}
		if(!flag)break;//Bellman-Ford优化
	}
	for(i=2;i<=n;i++)printf("%lld\n",dp[i][0]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值