PAT 1087 C++

1.题意

给出图的顶点,边信息。求出输入的起始点到ROM的最短距离,如果有不止一条路径,那么找出幸福值【幸福值的标记是通过每个顶点的幸福值确定的】最大的那条路径。

2.分析

  • 其实题目大都是相似的,主要是考察 dijkstra 算法,这个算法是用于计算单源最短路径,我们只需要计算从 staLocROM上的最短路径即可,然后记录其每个路径的最大幸福值;同时记录最短路径条数。最后输出即可。
  • 但问题的转弯在于,如何将这个字母表示的GDN,和BLN等转换成顶点下标?这个可以使用map轻松实现。一个 map 用于维护 string -> int;一个map用于维护int-> string这样就可以在输出时根据int 输出 string了。

这里,针对题目的测试用例可以得到如下的图示:
PAT 1087 C++版_c

3.代码

#include<cstdio> 
#include<algorithm>
#include<iostream>
#include<map>
#define maxn 205
#define INF 0x3fffffff 

using namespace std;

int G[maxn][maxn];
int dis[maxn];
int way[maxn];
int hap[maxn];
int vis[maxn];
int num[maxn]; //存储起始点到每个节点的幸福值 
int roa[maxn]; //到节点的路径条数 
int N,K;

void init(){
	fill(G[0],G[0]+maxn*maxn,INF);//最大值为INF 
	fill(way,way+maxn,-1);//初始化的路径为-1 
	fill(dis,dis+maxn,INF);
	fill(vis,vis+maxn,0);
	fill(num,num+maxn,0);
	fill(roa,roa+maxn,0);
}

//开始dijkstra 算法 
void dijkstra(int s){
	dis[s] = 0;
	way[s] = 0;
	num[s] = 0;
	roa[s] = 1; //初始情况下,到自身的距离为 1 
	int i,j,v;
	for(i = 0;i< N;i++){
		int MIN = INF,u= -1;
		for(j =0;j< N;j++){
			if(vis[j]==0 && dis[j] < MIN){
				MIN = dis[j]; 
				u = j;
			}
		} 
		
		vis[u]  = 1;//u节点已访问 
		//找出节点 
		for(v = 0;v < N;v++){
			if(vis[v]==0 && G[u][v] !=INF ){
				if(dis[v] > dis[u] + G[u][v] ) {
					way[v] = u; //更新路径前驱
					dis[v] = dis[u] + G[u][v];//更新最短路径
					num[v] = num[u] + hap[v]; 
					roa[v] = roa[u]; 
				}
				
				else if(dis[v] == dis[u] + G[u][v] ) { //如果最短路径相同 					
					roa[v] += roa[u];
					if( num[v] - hap[v] < num[u]) {//如果幸福值比较小
						num[v] = hap[v] + num[u];//更新幸福值 
						way[v] = u;//更新路径前驱
					}
				}
			}	
		}
	}
}

int main(){
	cin >> N >>K;
	map<string,int> stoi;
	map<int,string> itos;
	
	int i,j;
	string staLoc;//开始的位置 
	cin >> staLoc;
	
	stoi[staLoc] = 0;//将初始点加入到 map中 
	itos[0]= staLoc;
	
	string tempLoc;
	int tempHap;
	init();
	for(i = 1;i< N ;i++){
		cin >> tempLoc >> tempHap;
		stoi[tempLoc]= i;
		itos[i] = tempLoc;
		hap[i] = tempHap;//加入每个节点的幸福值		 		
	}
	
	string loc1,loc2;
	int len;
	for(i = 0;i< K;i++){
		cin >> loc1 >> loc2 >> len;
		G[stoi[loc1]][stoi[loc2]] = len;
		G[stoi[loc2]][stoi[loc1]] = len;
	} 
	
	dijkstra(0);
	int des = stoi["ROM"];//得到 ROM 的下标 
	//cout <<"des = "<<des<<"\n";
	int tempDes = des;
	int index = 0;//路径数 
	int anw[maxn]; 
	
	while(tempDes!=way[tempDes]){	
		anw[index]  = tempDes;
		index ++;
		tempDes = way[tempDes];
	}
	anw[index] = tempDes;//开始的起点城市 
	cout << roa[des] <<" "<< dis[des] <<" " << num[des] << " "<<num[des]/index <<"\n";
	
	for(i = index ;i>=0;i--){
		cout << itos[anw[i]] ;
		if(i != 0) cout <<"->"	;
	}	
}

4.测试用例

6 7 HZH
ROM 100
PKN 40
GDN 55
PRS 95
BLN 80
ROM GDN 1
BLN ROM 1
HZH PKN 1
PRS ROM 2
BLN HZH 2
PKN GDN 1
HZH PRS 1

5.总结

在写dijkstra算法时,其注意点有如下几个:

  • 注意if - else ifif - if的区别
if(vis[v]==0 && G[u][v] !=INF ){
	···
}			
else if(dis[v] == dis[u] + G[u][v] ) { //如果最短路径相同 		

而不是

if(vis[v]==0 && G[u][v] !=INF ){
	···
}			
if(dis[v] == dis[u] + G[u][v] ) { 

否则会得到一个错误的答案。