CSU 1891: Full Tank?(dijkstra+priority_queue or dp?)

1891: Full Tank?

        Time Limit: 1 Sec     Memory Limit: 128 Mb     Submitted: 57     Solved: 11    


Description

After going through the receipts from your car trip through Europe this summer, you realised that the gas prices varied between the cities you visited. Maybe you could have saved some money if you were a bit more clever about where you filled your fuel?
To help other tourists (and save money yourself next time), you want to write a program for finding the cheapest way to travel between cities, filling your tank on the way. We assume that all cars use one unit of fuel per unit of distance, and start with an empty gas tank.

Input

The first line of input gives 1<=n<=1000 and 0<=m<=10000, the number of cities and roads. Then follows a line with n integers 1<=pi<=100, where pi is the fuel price in the ith city. Then follow m lines with three integers 0<=u, v < n and 1<=d<=100, telling that there is a road between u and v with length d. Then comes a line with the number 1<=q<=100, giving the number of queries, and q lines with three integers 1<=c<=100, s and e, where c is the fuel capacity of the vehicle, s is the starting city, and e is the goal.

Output

For each query, output the price of the cheapest trip from s to e using a car with the given capacity, or “impossible” if there is no way of getting from s to e with the given car.

Sample Input

5 5
10 10 20 12 13
0 1 9
0 2 8
1 2 1
1 3 11
2 3 7
2
10 0 3
20 1 4

Sample Output

170
impossible

Hint

Source

2007 Nordic Collegiate Programming Contest


        看到这题很容易想到是一个最短路模型,我的第一想法是和第一次多校赛的一道充电汽车很像,然后往floyd方向上去想。floyd的话处理impossible非常的简洁,与我说的那题一样,求个两点间最长边即可。可能你会说1000个点floyd会tle,但是这题只有10000条边,用上边表优化floyd,也是可以过的,这个KD的同学应该听我讲过的。或许这题就用floyd也可以过?我还没有时间去尝试。

        好了回归正题,此题正解还是要用dijkstra+prioity_queue的。很遗憾当时只想着去暴搜(不过赛后还是没有借助任何帮助a了这题),没有看出总状态数只有1000*101个,即点数*油箱最大容量。。然后可以直接把每一个状态看作一个点,点之间的联系有两种:一是直接加油,每次加一个单位的油,费用为对应单价;二是在不同城市之间的转移,费用为0,前提是油量足够。举一个例子,对于当前状态d[uu][vv]表示到达uu城市、剩余油量为vv时的最小花费,它可以转移到d[uu][vv+1](相当于加油)或者d[nxt][vv-w](相当于从uu城市到了nxt城市、相应油量也要减少)。这样的话,就变成了一个裸的最短路模型,用dijkstra+priority_queue即可。

        总的来说此题模型较为灵活,是一道检验最短路模型的一道非常好的题目。然后由于我的模板还没有经过检验,所以还是花了一些时间调试的,不过也好,检验之后现在的模板应该是正确的。

        哦,突然灵感突发,好像所有的最短路的本质其实是一个图上的dp,而这题恰巧非常像一个用优先队列优化的dp,连转移方程都很清楚。dp[i][j]表示走到i城市剩余油量为j所需要的最小花费,w[i][j]代表在图上从i到j需要耗费的油,p[i]代表在i城市加一单位油的花费。转移方程有两个:dp[k][j-w[i][k]]=min(dp[k][j-w[i][k]],dp[i][j])表示从i城市走到k城市,dp[i][j+1]=min(dp[i][j+1],dp[i][j]+p[i]),表示在i城市加油。但是呢直接去用dp做的话代价还是很高的,所以用这种最短路的方式进行优化。具体代码如下:

#include<bits/stdc++.h>
#define MAX_V 1010
#define INF 1e9

using namespace std;

struct Edge
{
	int y,w;
};

struct Node
{
	int x,y,w;
};

int d[MAX_V][110],p[MAX_V],sm[MAX_V],n,m,quest;
vector<Edge> g[MAX_V];
bool v[MAX_V][110];

struct cmp
{
	bool operator()(Node a,Node b)
	{
		return a.w>b.w;
	}
};

inline int dijkstra(int s,int t,int oil)
{
	priority_queue<Node,vector<Node>,cmp> q;
	for(int i=0;i<n;i++)
		for(int j=0;j<=oil;j++)
			v[i][j]=0,d[i][j]=INF;
	d[s][0]=0; Node node;
	node.x=s; node.y=0; node.w=0;
	q.push(node);
	while (!q.empty())
	{
		Node j=q.top();
		q.pop(); int uu=j.x,vv=j.y;
		if (uu==t) return d[uu][0];			//最优解肯定是到达终点刚好用完所有油的情况
		if (vv+1<=oil && d[uu][vv+1]>d[uu][vv]+p[uu])		//相当于加油
		{
			d[uu][vv+1]=d[uu][vv]+p[uu];
			node.x=uu; node.y=vv+1; node.w=d[uu][vv+1];
			q.push(node);
		}
		if (vv>=sm[uu])				//小剪枝,如果当前油量到不了最近的城市就直接跳过
			for(int k=0;k<g[uu].size();k++)
			{
				int can=vv-g[uu][k].w;
				int nxt=g[uu][k].y;
				if (can>=0 && d[nxt][can]>d[uu][vv] && !v[nxt][can])	//如果某一状态未访问切现在访问更有则更新
				{
					v[nxt][can]=1;
					d[nxt][can]=d[uu][vv];
					node.x=nxt; node.y=can; node.w=d[uu][vv];	//开车过去只消耗油不增加花费
					q.push(node);
				}
			}
	}
	return d[t][0];
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) 
	{
		scanf("%d",&p[i]);
		sm[i]=INF;
	}
	for(int i=1;i<=m;i++)
	{
		Edge point; int x,y; scanf("%d%d%d",&x,&y,&point.w);
		point.y=y; g[x].push_back(point);
		point.y=x; g[y].push_back(point);
		sm[x]=min(sm[x],point.w); 
		sm[y]=min(sm[y],point.w);
	}
	scanf("%d",&quest);
	while (quest--)
	{
		int w,s,t; scanf("%d%d%d",&w,&s,&t);
		int ans=dijkstra(s,t,w);
		if (ans!=INF) printf("%d\n",ans);
		         else puts("impossible"); 
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值