PAT甲级 Public Bike Management(30)

题目描述
There is a public bike service in Hangzhou City which provides great convenience to the tourists from all over the world. One may rent a bike at any station and return it to any other stations in the city.
The Public Bike Management Center (PBMC) keeps monitoring the real-time capacity of all the stations. A station is said to be in perfect condition if it is exactly half-full. If a station is full or empty, PBMC will collect or send bikes to adjust the condition of that station to perfect. And more, all the stations on the way will be adjusted as well.
When a problem station is reported, PBMC will always choose the shortest path to reach that station. If there are more than one shortest path, the one that requires the least number of bikes sent from PBMC will be chosen.
在这里插入图片描述
Figure 1
Figure 1 illustrates an example. The stations are represented by vertices and the roads correspond to the edges. The number on an edge is the time taken to reach one end station from another. The number written inside a vertex S is the current number of bikes stored at S. Given that the maximum capacity of each station is 10. To solve the problem at S3, we have 2 different shortest paths:
1 .PBMC -> S1 -> S3. In this case, 4 bikes must be sent from PBMC, because we can collect 1 bike from S1 and then take 5 bikes to S3, so that both stations will be in perfect conditions.
2 .PBMC -> S2 -> S3. This path requires the same time as path 1, but only 3 bikes sent from PBMC and hence is the one that will be chosen.

输入描述:
Each input file contains one test case. For each case, the first line contains 4 numbers: Cmax (<= 100), always an even number, is the maximum capacity of each station; N (<= 500), the total number of stations; Sp, the index of the problem station (the stations are numbered from 1 to N, and PBMC is represented by the vertex 0); and M, the number of roads. The second line contains N non-negative numbers Ci (i=1,…N) where each Ci is the current number of bikes at Si respectively. Then M lines follow, each contains 3 numbers: Si, Sj, and Tij which describe the time Tij taken to move betwen stations Si and Sj. All the numbers in a line are separated by a space.

输出描述:
For each test case, print your results in one line. First output the number of bikes that PBMC must send. Then after one space, output the path in the format: 0->S1->…->Sp. Finally after another space, output the number of bikes that we must take back to PBMC after the condition of Sp is adjusted to perfect.
Note that if such a path is not unique, output the one that requires minimum number of bikes that we must take back to PBMC. The judge’s data guarantee that such a path is unique.

输入例子:
10 3 3 5
6 7 0
0 1 1
0 2 1
0 3 3
1 3 1
2 3 1

输出例子:
3 0->2->3 0

题目大意


有一个共享自行车系统,由一个中心(PBMC)和N个站点组成。每个站点能够停放的自行车数量为Cmax,而当站点的实时自行车数S i为Cmax/2时,称这个站点是perfect的。图的边权是站点之间的距离,当一个站点被上报需要调整的时候,PBMC就会开着一辆车搭载若干自行车,找一条最短路径到目的站点调整自行车数,并且 在去的过程中,沿途的站点也会一起被调整至perfect。最后带着多余的自行车从目的站点回来。如果最短路径不唯一,那么就选择出发的时候需要带最少自行车的路径。如果这样的路径也不唯一,那么就选择带回来的自行车数量最少的路径。题目保证这样的路径是唯一的。

思路


由于是最短路径并且是由一个点到其余点的,很容易就想到Dijkstra算法,但要注意的是,一般的Dijkstra算法只能计算到一点的某一条最短路径,所以我们需要对算法进行一些小改动,让我们能够获得所有的最短路径。

并且获取到某一点的所有最短路径之后,我们还要从中筛选出一条最优的最短路径,也就是优先选择需要携带自行车数量最少的路径,其次优先选择需要带回的自行车数量最少的路径。

至于Dijkstra算法,这里就不多提了,就是一种每次都找距离次远的下一个点的贪心算法。

多最短路径

一般我们做Dijkstra都是用一个int pre[N]数组来进行回溯,那么当可能出现多条最短路径的时候,我们只需要把这个数组类型改为vector<int> pre[500],就能够存储很多个前驱结点,这样就能够保存多条路径了。

路径筛选


在这里插入图片描述
假设Cmax为100(perfect=50),’-'代表PBMC,有这样两条由PBMC到②的最短路径,我们选择用两个变量send和collect记录出发时需要携带的自行车数和返回时回收的自行车数量,初始化均为0。

来看看路径A(橙色路径):从-出发到0,由于0站点的实时数量为63,调整数量等于50-63=-13,也就是多出13辆,所以collcet+=13。Send/Collect更新为如图所示。接下来0->2,2的实时数量为45,调整数量=50-45=5,也就是缺5辆,并且由于此时运载车的collect为13,因此这5辆我们不需要从-带出来,只需要从collect中扣除即可,然后更新数据。

再看看路径B(蓝色路径):从-出发到1,调整数量50-43=7,send=send+7=0+7=7;1->2,调整数量50-45=5,send=send+5=7+5=12。

然后对比两条路径的send和collect,就能够选择出优先级最高的A了。

这个筛选路径的步骤,关键点是它的send和collect是一步步规划更新出来的,而不是简单的用

Cmax/2*站点数-路径上站点自行车数之和

就可以的,因为题目要求的是去的路上才能调整,回来的路上不能调整。

代码

#include <iostream>
#include<set>
#include<vector>
using namespace std;
int C, N, bikes[501], Sp, M, map[501][501]{ 0 }, dist[501]{ -1 };
vector<int> pre[500];
set<int>  _set;
//根据pre将每条最短路径添加到paths中
void getShortesPath(int v, vector<int> path,vector<vector<int>> &paths) {
	path.insert(path.begin(), v);
	if (!v) {
		paths.push_back(path);
	}
	for (vector<int>::iterator it = pre[v].begin(); it != pre[v].end(); it++) {
		getShortesPath(*it, path, paths);
	}
}
//获得某条路径的send和collect
pair<int,int> getSandC(vector<int> path) {
	int x = 0, y = 0;
	int p = C / 2;
	for (int i = 1; i < path.size(); i++) {
		int bike = bikes[path[i]];
		if (bike > p) y += bike - p;
		else {
			int need = p - bike;
			x += y < need ? need - y : 0;
			y -= y > need ? need : y;

		}
	}
	return pair<int, int>(x, y);
}
int main() {
	dist[0] = 0;
	cin >> C >> N >> Sp >> M;
	for (int i = 1; i <= N; i++) cin >> bikes[i];
	for (int i = 0; i < M; i++) {
		int v1, v2, l;
		cin >> v1 >> v2 >> l;
		map[v1][v2] = map[v2][v1] = l;
	}
	int _N = N;
	for (int i = 1; i <= N; i++) {
		int j =1;
		for (; j <= N; j++) {
			if (map[i][j]) break;
		}
		if (j > N) _N--;
	}
	_set.insert(0);
	int inset = 0;
	//改造了的Dijkstra获取所有最短路径
	//注意终止条件不把非连通结点算在内 不然会死循环
	while ((_N - inset)) {
		int  len = ~(1 << 31);
		set<int> to;
		for (set<int>::iterator it = _set.begin(); it != _set.end(); it++) {
			for (int i = 0; i <= N; i++) {
				if (i == *it) continue;
				if (map[*it][i] && map[*it][i] + dist[*it] < len && !_set.count(i)) {
					len = map[*it][i] + dist[*it];
				}
			}
		}
		for (set<int>::iterator it = _set.begin(); it != _set.end(); it++) {
			for (int i = 0; i <= N; i++) {
				if (i == *it) continue;
				if (map[*it][i] && map[*it][i] + dist[*it] == len && !_set.count(i)) {
					dist[i] = map[*it][i] + dist[*it];
					pre[i].push_back(*it);
					to.insert(i);
				}
			}
		}
		for (set<int>::iterator it = to.begin(); it != to.end(); it++) {
			_set.insert(*it);
			inset++;
		}
	}
	vector<vector<int>> paths;
	getShortesPath(Sp, vector<int>(), paths);
	pair<int, int>* p = new pair<int, int>[paths.size()];
	bool* match = new bool[paths.size()];
	for (int i = 0; i < paths.size(); i++) {
		p[i] = getSandC(paths[i]);
		match[i] = false;
	}
	//根据所有最短路径的send和collect筛选最优路径
	int min_send = ~(1 << 31),min_collect = ~(1 << 31),idxOfPath=-1;
	for (int i = 0; i < paths.size(); i++)  if (p[i].first < min_send) min_send = p[i].first;
	for (int i = 0; i < paths.size(); i++)	if (p[i].first == min_send) match[i] = true;
	for (int i = 0; i < paths.size(); i++) {
		if (match[i] && p[i].second < min_collect) {
			min_collect = p[i].second;
			idxOfPath = i;
		}
	}
	cout << p[idxOfPath].first<<" ";
	vector<int> BestPath = paths[idxOfPath];
	for (int i = 0; i < BestPath.size(); i++) {
		cout << BestPath[i] << (i == BestPath.size() - 1 ? "" : "->");
	}
	cout << " " << p[idxOfPath].second;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值