NOI 4.3 图论 726: ROADS

题目来源:http://noi.openjudge.cn/ch0403/726/

726:ROADS

总时间限制1000ms   内存限制65536kB

描述

N cities named with numbers 1 ... N are connected with one-wayroads. Each road has two parameters associated with it : the road length andthe toll that needs to be paid for the road (expressed in the number of coins). 
Bob and Alice used to live in the city 1. After noticing that Alice wascheating in the card game they liked to play, Bob broke up with her and decidedto move away - to the city N. He wants to get there as quickly as possible, buthe is short on cash. 

We want to help Bob to find theshortest path from the city 1to the city N that he canafford with the amount ofmoney he has. 

输入

The first line of the input contains the integer K, 0 <= K<= 10000, maximum number of coins that Bob can spend on his way. 
The second line contains the integer N, 2 <= N <= 100, the total numberof cities. 

The third line contains the integer R, 1 <= R <= 10000, the total numberof roads. 

Each of the following R lines describes one road by specifying integers S, D, Land T separated by single blank characters : 

  • S is the source city, 1 <= S <= N 
  • D is the destination city, 1 <= D <= N 
  • L is the road length, 1 <= L <= 100 
  • T is the toll (expressed in the number of coins), 0 <= T <=100


Notice that different roads may have the same source and destination cities.

输出

The first and the only line of the output should contain thetotal length of the shortest path from the city 1 to the city N whose totaltoll is less than or equal K coins. 
If such path does not exist, only number -1 should be written to the output. 

样例输入

5
6
7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2

样例输出

11

来源

CEOI 1998

 -----------------------------------------------------

思路

题意:

给出一个图和起始点、终止点,图中边不仅有权值还有费用,求满足费用约束条件的从起始点到终止点的最短路。

Dijkstra最短路变形题。

由于有重边,所有不能用邻接矩阵存储图,这里采用的是正向表(又叫“前向星”,forward star representation),由于输入的边关于始端是乱序的,所以只能先得到边列表,再通过边列表算出正向表的A数组(A[i]表示以i为始端的边的末端在B数组中的上界)。

同样由于有重边,只能采用基于边的堆优化的Dijkstra算法。同时,由于满足收费小于k的最优解不是全局最优解,故定理“最短路的子路径也是最短路”不成立,一个节点的最短路是不能确定的(因为还有收费的问题),所以要取消vis数组,对一个节点找到最短路后仍然允许改变。

-----------------------------------------------------

代码

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;

const int NMAX = 105;
const int MMAX = 10005;

struct node {
	int a,b,l,t;

	node (int aa, int bb, int ll, int tt): a(aa),b(bb),l(ll),t(tt){}

	bool operator< (const node &nd) const					// 始端升序
	{
		return a < nd.a;
	}
};

struct dijk {
	int des, len, toll;					// des: 目的地, len: 起始点到目的地的路径长度, toll: 起始点到目的地的花费

	dijk(int dd, int ll, int tt): des(dd), len(ll), toll(tt){}

	bool operator< (const dijk &dstr) const
	{
		return len > dstr.len;
	}
};

int A[NMAX] = {};						// 正向表数组
int fee[NMAX] = {};						// 起始点到当前点的费用
priority_queue<dijk> q;					// dijkstra堆

int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin ("0403_726.txt");
	int k,n,r,i,s1,s2,ll,tt,cnt=0,minid,minl,mint;
	vector<node> vec;					// 边列表
	fin >> k >> n >> r;
	for (i=0; i<r; i++)
	{
		fin >> s1 >> s2 >> ll >> tt;
		s1--; s2--;
		node nd(s1,s2,ll,tt);
		vec.push_back(nd);
	}
	fin.close();
	sort(vec.begin(), vec.end());								// 按始端升序排列
	for (i=0; i<r; i++)
	{
		node nd = vec.at(i);
		A[nd.a] = ++cnt;										// 根据边列表求正向表数组
	}
	for (i=0; i<A[0]; i++)										// 初始化Dijkstra堆
	{
		node nd = vec.at(i);
		dijk dkst(nd.b, nd.l, nd.t);
		q.push(dkst);
	}
	bool has = false;											// 是否有解
	while (!q.empty())
	{
		dijk dkst = q.top();
		q.pop();
		if (dkst.toll>k)						// 如果已经访问过或费用已经超过k
		{
			continue;											// 则进入新循环取下一个点
		}
		minid = dkst.des;
		minl = dkst.len;
		mint = dkst.toll;
		if (minid == n-1)
		{
			cout << minl;
			has = true;
			break;
		}
		for (i=A[minid-1]; i<A[minid]; i++)						// 更新与minid相邻的节点
		{
			node nd = vec.at(i);
			dijk ndkst(nd.b, nd.l+minl, nd.t+mint);
			q.push(ndkst);
		}
	}
	if (!has)
	{
		cout << -1;
	}
	return 0;
#endif
#ifdef ONLINE_JUDGE
	int k,n,r,i,s1,s2,ll,tt,cnt=0,minid,minl,mint;
	vector<node> vec;					// 边列表
	cin >> k >> n >> r;
	for (i=0; i<r; i++)
	{
		cin >> s1 >> s2 >> ll >> tt;
		s1--; s2--;
		node nd(s1,s2,ll,tt);
		vec.push_back(nd);
	}
	sort(vec.begin(), vec.end());								// 按始端升序排列
	for (i=0; i<r; i++)
	{
		node nd = vec.at(i);
		A[nd.a] = ++cnt;										// 根据边列表求正向表数组
	}
	for (i=0; i<A[0]; i++)										// 初始化Dijkstra堆
	{
		node nd = vec.at(i);
		dijk dkst(nd.b, nd.l, nd.t);
		q.push(dkst);
	}
	bool has = false;											// 是否有解
	while (!q.empty())
	{
		dijk dkst = q.top();
		q.pop();
		if (dkst.toll>k)						// 如果已经访问过或费用已经超过k
		{
			continue;											// 则进入新循环取下一个点
		}
		minid = dkst.des;
		minl = dkst.len;
		mint = dkst.toll;
		if (minid == n-1)
		{
			cout << minl;
			has = true;
			break;
		}
		for (i=A[minid-1]; i<A[minid]; i++)						// 更新与minid相邻的节点
		{
			node nd = vec.at(i);
			dijk ndkst(nd.b, nd.l+minl, nd.t+mint);
			q.push(ndkst);
		}
	}
	if (!has)
	{
		cout << -1;
	}
	return 0;
#endif
}


猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/80743941
4.3