1. 程式人生 > 實用技巧 >PAT - 甲級 - 1018 Public Bike Management

PAT - 甲級 - 1018 Public Bike Management

Nothing to fear


種一棵樹最好的時間是十年前,其次是現在!

那些你早出晚歸付出的刻苦努力,你不想訓練,當你覺的太累了但還是要咬牙堅持的時候,那就是在追逐夢想,不要在意終點有什麼,要享受路途的過程,或許你不能成就夢想,但一定會有更偉大的事情隨之而來。 mamba out~

2020.7.14


人一我十,人十我百,追逐青春的夢想,懷著自信的心,永不言棄!

Public Bike Management

考點: Dijkstra求最短路,Dijkstra記錄最短路徑,dfs,閱讀理解

題目大意

給定一個起點PBMC,和一個終點SP,求出從PBMC 到 Sp的最短路徑(如果不止一條則都要記錄下來),從中挑選出一條,從PBMC需要傳送的車輛最少的那一條路徑,如果遇到重複則輸出需要將自行車返回回去的儘可能少的那一條(題目確保該路徑唯一)。

分析

1.如何記錄在Dijkstra中所求得得最短路徑,由於Dijkstra時基於貪心思想當你要利用某一個頂點 x 去鬆弛其他頂點時,x之前得結點已經被確定,換句話說就是每當你確定一個點,這個點得前一個點一定已經被確定,雖然聽起來像廢話但是還是需要去好好考慮的!故我們可以用一個容器來存在某個點的前一個點來記錄路徑

定義一個容器vector<int> pre[N]; 用於存放結點的所確定的前一個結點有哪些
while(!q.empty())
	{
		int x = q.top().second;q.pop();
		if(vis[x])continue;
		vis[x] = 1;
		for(int i = 1;i <= n;i ++)
		{
			int y = i , w = e[x][i];
			if(dis[y] > dis[x] + w) // 更新 x -> y
			{
				pre[y].clear();
				pre[y].push_back(x);
				dis[y] = dis[x] + w;
				q.push({-dis[y] , y});
			}else if(dis[y] == dis[x] + w){
				pre[y].push_back(x);
			}
		}
	}

2.得到路徑之後需要對路徑進行dfs檢查所需要傳送/放回都達到最優的那一條路!

需要注意的點:

  • 認真讀題
  • 如何記錄Dijkstra的最短路徑的結點需要熟記利用記錄前一個結點的pre容器!

完整程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1005;
int Cmax , n , m , sp , mid , minsent = 0x3f, mintake = 0x3f;
int e[N][N] , dis[N] , carry[N];
bool vis[N];
priority_queue<pair<int,int> > q;
vector<int> pre[N] , path;
void dfs(vector<int> tmp, int now)
{
	if(now == 0)
	{
		int need = 0 , back = 0;
		for(int i = tmp.size() - 1;i >= 0;i --)
		{
			int id = tmp[i];
			if(carry[id] > 0)
				back += carry[id];
			else{
				if(back > (0 - carry[id]))
					back += carry[id];
				else{
					need += (0 - carry[id] - back);
					back = 0;
				}
			}
		}
		if(need < minsent){
			minsent = need;
			mintake = back;
			path = tmp;
		}
		if(need == minsent && back < mintake)
		{
			mintake = back;
			path = tmp;
		}
	}
	for(int i = 0;i < pre[now].size();i ++)
	{
		tmp.push_back(now);
		dfs(tmp, pre[now][i]);
		tmp.pop_back();
	}
}
void Dijkstra()
{
	memset(dis , 0x3f , sizeof dis);
	memset(vis , 0 ,sizeof vis);
	dis[0] = 0; // 規定PBMC 為頂點 0
	q.push({0 , 0});
	while(!q.empty())
	{
		int x = q.top().second;q.pop();
		if(vis[x])continue;
		vis[x] = 1;
		for(int i = 1;i <= n;i ++)
		{
			int y = i , w = e[x][i];
			if(dis[y] > dis[x] + w) // 更新 x -> y
			{
				pre[y].clear();
				pre[y].push_back(x);
				dis[y] = dis[x] + w;
				q.push({-dis[y] , y});
			}else if(dis[y] == dis[x] + w){
				pre[y].push_back(x);
			}
		}
	}
	vector<int> t;dfs(t, sp);
}
int main()
{
	memset(e , 0x3f , sizeof e);
	for(int i = 0;i <= n;i ++)e[i][i] = 0;
	cin >> Cmax >> n >> sp >> m;
	mid = Cmax / 2;
	for(int i = 1;i <= n;i ++)
		scanf("%d",&carry[i]) , carry[i] = carry[i] - mid;
	int a, b ,c;
	for(int i = 0;i < m;i ++)
	{
		scanf("%d %d %d", &a ,& b, &c);
		e[a][b] = e[b][a] = c;
	}
	Dijkstra();
	cout << minsent << " 0";
	for(int i = path.size() - 1;i >= 0;i --)
	{
		printf("->%d", path[i]);
	}
	cout << " " << mintake << endl;
	return 0;
}