2010 TCO Algorithm Online Round 5 - Division I, Level Two LongJourney

本文介绍了一种求解从起点到终点最小成本路径的问题,通过改进的状态定义和使用Dijkstra算法来解决具有特定条件的图中寻找最优解。适用于节点数量较小但油箱容量较大的情形。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个人有一辆车,车有一个油箱,油箱有油量上限,走每条边需要消耗油,每个点可以加油,价格不等。问0到1的最小代价。

以前做这种题是定义d[i][j]当前在第i个点,有j升油,所需的最小代价。现在学了一个新招。

我们知道,在第i个点,到第j个点,有两种加油策略,第一种是加一定的油,使得到j时油为0。第二种是加满油,因为i点的油比较便宜。定义d[i][j]为当前在d第i个点,上一次是第j的点,且在第j的点把油加满,此时的最小代价。在加一个特殊状态d[i][#]表示当前在第i个点,且油为0的最小代价。这样我们拿到一个状态就能算出现在还有多少油。先预处理出两两点对间的最短路就可以从(0,#)到(1,#)求最小代价了。状态n^2,用Dijkstra复杂度n^3logn。本题n小油箱大,适合用这种方法。

#include <bits/stdc++.h>
#define maxn 109
using namespace std;
const int INF=1e9;
const long long inf=1e18;
int n,d[maxn][maxn],mx;
bool inq[maxn],done[maxn][maxn];
long long dis[maxn][maxn],wi[maxn];
char S[100009];
struct node
{
	int now,pre;
	long long d;
	bool operator<(const node &rhs)const
	{
		return d>rhs.d;
	}
	node(int now,int pre,long long d)
	{
		this->now=now;
		this->pre=pre;
		this->d=d;
	}
};
priority_queue<node>Q;
long long Dijkstra()
{
	for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dis[i][j]=inf;
	memset(done,0,sizeof(done));
	while(!Q.empty())Q.pop();
	dis[0][n]=0;
	Q.push(node(0,n,0));
	while(!Q.empty())
	{
		int now=Q.top().now,pre=Q.top().pre;
		Q.pop();
		int fuel;
		if(pre!=n)
			fuel=mx-d[pre][now];
		else
			fuel=0;
		//printf(">>%d %d %lld %d\n",now,pre,dis[now][pre],fuel);
		if(done[now][pre])
			continue;
		done[now][pre]=1;
		if(now==1)
			return dis[now][pre];
		for(int next=0;next<n;next++)
		{
			if(d[now][next]>mx)
				continue;
			if(dis[next][now]>dis[now][pre]+(mx-fuel)*wi[now])
			{
				dis[next][now]=dis[now][pre]+(mx-fuel)*wi[now];
				Q.push(node(next,now,dis[next][now]));
			}
			if(fuel<d[now][next])
			{
				if(dis[next][n]>dis[now][pre]+(d[now][next]-fuel)*wi[now])
				{
					dis[next][n]=dis[now][pre]+(d[now][next]-fuel)*wi[now];
					Q.push(node(next,n,dis[next][n]));
				}
			}
		}
	}
	return -1;
}
class LongJourney
{
public: long long minimumCost(vector <int> fuelPrices, int fuelTank, vector <string> roads)
	{
		n=fuelPrices.size();
		mx=fuelTank;
		int len=0;
		for(int i=0;i<(int)roads.size();i++)
		{
			for(int j=0;j<(int)roads[i].length();j++)
			{
				S[len++]=roads[i][j];
			}
		}
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				if(i==j)
					d[i][j]=0;
				else 
					d[i][j]=INF;
		char *p=strtok(S," ");
		while(p)
		{
			int cur=0,last=0,u,v,w;
			for(;p[cur]!=',';cur++);
			p[cur]=0; u=atoi(p+last);p[cur]=',';last=cur+1;cur++;
			for(;p[cur]!=',';cur++);
			p[cur]=0;v=atoi(p+last);p[cur]=',';last=cur+1;
			w=atoi(p+last);
			d[u][v]=min(d[u][v],w);
			d[v][u]=min(d[v][u],w);
			p=strtok(NULL," ");
		}
		for(int k=0;k<n;k++)
			for(int i=0;i<n;i++)
				for(int j=0;j<n;j++)
					d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
		for(int i=0;i<n;i++)wi[i]=fuelPrices[i];
		return Dijkstra();
	}
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值