CSU1232 懒汉的旅行 bfs+优先队列

32 篇文章 0 订阅

题目大意:懒汉需要从A城市开车到B城市去见一个朋友,他的汽车的邮箱的容积L升,车每行驶1公里就要消耗1升油。途中只有在到达一个城市之后才可以找的到加油站并加油,而且懒汉的车很特殊,每次加油只能加满整个邮箱。

懒汉希望途中加油的次数越少越好,当加油的次数相同时,他希望加油所花的钱数越少越好。现在懒汉把地图告诉了你,你能帮帮他吗?可以认为懒汉出发的时候油箱是满的,如果到达 B城市时油箱不是满的,也无需再加油。

假定各地加油站的汽油价格均为Y元/升,如果懒汉想从x城市沿长为z公里的路开车到达y城市,那么离开x城市时汽车至少应还有z升汽油。

Input
输入包含若干组数据,每组数据的第一行有四个正整数N(1<=N<=100),M(1<=M<=1000),L(1<=L<=100),Y(1<=Y<=100),其中N表示地图上一共有N个城市,标号分别为1,2,…,N,M表示N个城市间一共有M条路,L和Y的含义同上。

接下来一行有两个正整数S(1<=S<=N),T(1<=T<=N),分别表示A城市和B城市的标号。

接下来一共有M行,每行有三个正整数x(1<=x<=N),y(1<=y<=N),z(1<=z<=100),表示标号为x的城市和标号为y的城市之间有一条长为z公里的双向道路。

数据组数不超过100组,读入以EOF结束。

Output
对于每组数据,用一行输出一个整数,表示在加油次数最少的前提下,加油所花的最少的钱数。

如果懒汉不能开车到达B城市,输出-1即可。

Sample Input
4 4 6 1
1 4
1 2 3
2 4 4
1 3 2
3 4 6
Sample Output
2
思路:很明显要用到优先队列+bfs。(1)加油次数小的放在前面。(2)总花费少的放在前面。(3)剩余油量多的放在前面。关键是这题对于一个非终结状态最优判定是模糊的,比如说在同一个点 i i i,现在有三个状态:(1)加油次数 2 2 2,花费 120 120 120,剩余油量 0 0 0;(2)加油次数 3 3 3,花费 160 160 160;剩余油量 50 50 50;(3)加油次数 3 3 3,花费 200 200 200,剩余油量 100 100 100。设点 i i i距离终点的距离为 100 100 100公里,油价为 1 1 1元每公里,那么最优解其实是(3),次优解是(2),最差的解是(3)。是不是和自己预想的有些出入?因此这题对状态的筛选要宽松一点,只要加油次数、总花费、油量任意一个属性可更新,就应该把该状态放进队列中。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

struct Edge
{
	int to,nxt,dis;
}edge[2005];
int n,m,s,t,l,y,tot;
int head[105];

struct node
{
	int id,step,v,rest;
	node(){}
	node(int ii,int ss,int vv,int ll=l)
	{
		id=ii,step=ss,v=vv,rest=ll;
	}
	bool operator <(const node &a)const
	{
		if(step!=a.step)
			return step>a.step;
		if(v!=a.v)
			return v>a.v;
		if(rest!=a.rest)
			return rest<a.rest;
	}
}vis[105];

void addedge(int u,int v,int dis)
{
	edge[++tot].to=v,edge[tot].dis=dis,edge[tot].nxt=head[u],head[u]=tot;
}

void bfs()
{
	priority_queue<node> q;
	q.push(node(s,0,0));
	while(!q.empty())
	{
		node t1=q.top();
		q.pop();
		bool tag=0;
		if(t1.step<vis[t1.id].step)
			tag=1,vis[t1.id]=t1;
		else if(t1.v<vis[t1.id].v)
			tag=1,vis[t1.id]=t1;
		else if(t1.rest>vis[t1.id].rest)
			tag=1,vis[t1.id]=t1;
		if(!tag)
			continue;
		if(t1.id==t)
		{
			printf("%d\n",t1.v);
			return ;
		}
		for(int i=head[t1.id];i;i=edge[i].nxt)
		{
			if(edge[i].dis<=t1.rest)//不用加油
				q.push(node(edge[i].to,t1.step,t1.v,t1.rest-edge[i].dis));
			q.push(node(edge[i].to,t1.step+1,t1.v+(l-t1.rest)*y,l-edge[i].dis));
		}
	}
	printf("-1\n");
}
int main()
{
	while(~scanf("%d%d%d%d",&n,&m,&l,&y))
	{
		scanf("%d%d",&s,&t);
		int u,v,dis;
		tot=0;
		memset(head,0,sizeof(head));
		for(int i=1;i<=n;i++)
			vis[i]=node(i,INF,INF,0);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&u,&v,&dis);
			if(dis>l)
				continue;
			addedge(u,v,dis),addedge(v,u,dis);
		}
		bfs();
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值