PAT A1018 Public Bike Management(30)

题意

  • N个车站和一个管理中心,给定终点,选取管理中心到终点的最短路径,若有多条最短路径,要根据一些奇怪的标准从中选取一条唯一的最短路径(首先最小take其次最小return)。

注意

  1. 这道题肯定先要用dijkstra,然后问题来了,takereturn值在路径上不满足传递性!这就是说,给定终点x和到该点的最优路径0~x(最短路径里按照标准选取的唯一),其子问题(终点为0~x中间某点y)的最优路径不一定是0~x中的0~y,反例如下。

10 4 4 5 //原问题:0~4 //子问题:0~3
4 8 9 0
0 1 1
1 2 1
1 3 2
2 3 1
3 4 1

  1. 这道题还有个陷阱,其实没有说清。如下,车站的调整必须是从中心出发到目的车站连续的过程,返程不能调整。

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.

  1. 因为不满足传递性,所以不能在dijkstra中的更新路径部分应用标准,所以,要么记录前驱,之后dfs遍历多条最短路径;要么记录完整的最短路径,这样的话需要vector<vector<int>> p[Nmax],每个结点占有一个二维数组。

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <climits>
using namespace std;

const int Nmax = 501;
int Cmax, N, Sp, M, C2;
int bike[Nmax];
int road[Nmax][Nmax];
int take, rtn;

int dist[Nmax];
bool S[Nmax];
vector<vector<int>> p[Nmax];       

void DIJKSTRA(int s0)
{
	fill(S, S + Nmax, false);
	fill(dist, dist + Nmax, INT_MAX);
	dist[0] = 0;

	vector<int> v;
	v.push_back(0);
	p[0].push_back(v);

	for (int j = 0; j <= N; j++)
	{
		int min = INT_MAX;
		int k;
		for (int i = 0; i <= N; i++)
		{
			if (!S[i] && dist[i] < min)
			{
				min = dist[i];
				k = i;
			}
		}
		S[k] = true;
		for (int i = 0; i <= N; i++)
		{
			if (!S[i] && road[k][i] != INT_MAX)
			{
				if (dist[k] + road[k][i] < dist[i])
				{
					dist[i] = dist[k] + road[k][i];
					p[i].clear();                                 ////
					p[i] = p[k];
					for (int l = 0; l < p[i].size(); l++)
						p[i].at(l).push_back(i);
				}
				else if (dist[k] + road[k][i] == dist[i])
				{	
					for (int l = 0; l < p[k].size(); l++)
					{
						p[i].push_back(p[k].at(l));
						p[i].at(p[i].size() - 1).push_back(i);
					}
				}               ////多条最短路径全部记录
			}
		}
	}
}

int select_optimal_road(int sp)         
{
	int opt = -1;
	take = rtn = INT_MAX;
	vector<vector<int>> v = p[sp];
	for (int i = 0; i < v.size(); i++)
	{
		vector<int> v0 = v.at(i);
		int tk = 0, rt = 0;
		for (int j = 1; j < v0.size(); j++)
		{
			if (bike[v0.at(j)] > C2)
				rt += bike[v0.at(j)] - C2;
			else if(bike[v0.at(j)] < C2)
			{
				if (rt > C2 - bike[v0.at(j)])
					rt -= C2 - bike[v0.at(j)];
				else
				{
					tk += C2 - bike[v0.at(j)] - rt;
					rt = 0;
				}
			}
		}
		if (tk < take)
		{
			opt = i;
			take = tk;
			rtn = rt;
		}
		else if (tk == take && rt < rtn)
		{
			opt = i;
			rtn = rt;
		}
	}
	return opt;
}

int main()
{
	cin >> Cmax >> N >> Sp >> M;
	C2 = Cmax / 2;
	for (int i = 1; i <= N; i++)
		cin >> bike[i];
	int r1, r2;
	fill(road[0], road[0] + Nmax*Nmax, INT_MAX);
	for (int i = 0; i < M; i++)
	{
		cin >> r1 >> r2;
		cin >> road[r1][r2];
		road[r2][r1] = road[r1][r2];
	}
	DIJKSTRA(0);
	int o = select_optimal_road(Sp);
	vector<int> v = p[Sp].at(o);
	cout << take << " ";
	for (int i = 0; i < v.size(); i++)
	{
		if (i == 0)
			cout << v.at(i);
		else cout << "->" << v.at(i);
	}
	cout << " " << rtn;

	return 0;
}

dfs版本,注意这个dfs写法很有讲究
4月10日更新:这道题是需要对一条完整的最短路进行分析,不能中途比较。我们在最短路算法中得到了每个点的前驱表(都是最短路),然后就写一个dfs从终点出发,到了起点才能得到一条完整的最短路,然后再分析这条路。所以这个temp就用来存放dfs的当前行程。所以就有了两种写法(PAT A1004),temp存储 / 弹出当前点或者存储 / 弹出当前点的前驱点。下面的代码两种写法都有了,其中把前者注释掉了。


#include <iostream>
#include <algorithm>
#include <vector>
#include <climits>
using namespace std;

const int Nmax = 501;
int Cmax, N, Sp, M, C2;
int bike[Nmax];
int road[Nmax][Nmax];

int take = 1e9, rtn = 1e9;

int dist[Nmax];
bool S[Nmax];

vector<int> r[Nmax];
vector<int> ans, temp;

void DIJKSTRA(int s0)
{
	fill(S, S + Nmax, false);
	fill(dist, dist + Nmax, INT_MAX);
	dist[0] = 0;

	for (int j = 0; j <= N; j++)
	{
		int min = INT_MAX;
		int k;
		for (int i = 0; i <= N; i++)
		{
			if (!S[i] && dist[i] < min)
			{
				min = dist[i];
				k = i;
			}
		}
		S[k] = true;
		for (int i = 0; i <= N; i++)
		{
			if (!S[i] && road[k][i] != INT_MAX)
			{
				if (dist[k] + road[k][i] < dist[i])
				{
					dist[i] = dist[k] + road[k][i];
					r[i].clear();
					r[i].push_back(k);
				}
				else if (dist[k] + road[k][i] == dist[i])
					r[i].push_back(k);
			}
		}
	}
}

void select_optimal_road(int n)       // 正向计算中途不能和其他支路(也是最短路)比较,必须单独一条路算完之后再比较
{                                     // 该问题只支持正向计算(求出最短路后)
	//temp.push_back(n);

	if (n == 0)                       // 终于回到起点了,这样构成了一条完整的最短路
	{
		int tk = 0, rt = 0;
		for (int i = temp.size() - 2; i >= 0; i--)
		{
			if (bike[temp[i]] >= C2)
				rt += bike[temp[i]] - C2;
			else
			{
				if (rt >= C2 - bike[temp[i]])
					rt -= C2 - bike[temp[i]];
				else
				{
					tk += C2 - bike[temp[i]] - rt;
					rt = 0;
				}
			}
		}
		if (tk < take)
		{
			take = tk;
			rtn = rt;
			ans = temp;
		}
		else if (tk == take && rt < rtn)
		{
			rtn = rt;
			ans = temp;
		}

		//temp.pop_back();
		return;
	}
	for (int i = 0; i < r[n].size(); i++)
	{
		temp.push_back(r[n][i]);
		select_optimal_road(r[n][i]);
		temp.pop_back();
	}

	//temp.pop_back();
}

int main()
{
	cin >> Cmax >> N >> Sp >> M;
	C2 = Cmax / 2;
	for (int i = 1; i <= N; i++)
		cin >> bike[i];
	int r1, r2;
	fill(road[0], road[0] + Nmax*Nmax, INT_MAX);
	for (int i = 0; i < M; i++)
	{
		cin >> r1 >> r2;
		cin >> road[r1][r2];
		road[r2][r1] = road[r1][r2];
	}
	DIJKSTRA(0);

	temp.push_back(Sp);
	select_optimal_road(Sp);
	temp.pop_back();                            // 这句可有可无

	cout << take << " ";
	for (int i = ans.size() - 1; i >= 0; i--)
	{
		if (i == 0)
			cout << ans[i];
		else cout << ans[i] << "->";
	}
	cout << " " << rtn;

	return 0;
}

posted @ 2017-02-10 22:03  CrossingOver  阅读(126)  评论(0编辑  收藏  举报