All Roads Lead to Rome
题目描述
题目描述
Indeed there are many different tourist routes from our city to Rome. You are supposed to find your clients the route with the least cost while gaining the most happiness.输入描述:
Each input file contains one test case. For each case, the first line contains 2 positive integers N (2<=N<=200), the number of cities, and K, the total number of routes between pairs of cities; followed by the name of the starting city. The next N-1 lines each gives the name of a city and an integer that represents the happiness one can gain from that city, except the starting city. Then K lines follow, each describes a route between two cities in the format “City1 City2 Cost”. Here the name of a city is a string of 3 capital English letters, and the destination is always ROM which represents Rome.输出描述:
For each test case, we are supposed to find the route with the least cost. If such a route is not unique, the one with the maximum happiness will be recommended. If such a route is still not unique, then we output the one with the maximum average happiness – it is guaranteed by the judge that such a solution exists and is unique.
Hence in the first line of output, you must print 4 numbers: the number of different routes with the least cost, the cost, the happiness, and the average happiness (take the integer part only) of the recommended route. Then in the next line, you are supposed to print the route in the format “City1->City2->…->ROM”.输入例子:
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输出例子:
3 3 195 97
HZH->PRS->ROM
- 原题链接
题目分析
题目要求出起始点到目标点的所有最短路径,并选取其中欢乐值最大,或在欢乐值相等时经过节点最少的路径,要求输出的结果包含所有最短路径数,路径长度,欢乐值,平均欢乐值,以及路径信息。
一开始拿到这个题的时我首先想到的是DFS,找出所有从出发点到终点的路径,然后每次比较找出最短及条件最优的即可,但是严重超时,看了网上的其他大神的解法,发现还是自己对Dijkstra算法的理解不透彻,下面给出我的超时解法,以及计算最短路径题目很容易想到dijkstra算法给出的精妙解法,下面给出源码:
DFS解法
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main {
static int N, K;static int[][] costs;static int[] happiness;static boolean[] flag;static List<Integer> path;static int nums, minCost, maxJoy, avergeJoy;public static void main(String[] args) {Scanner s = new Scanner(System.in);N = s.nextInt();K = s.nextInt();Map<String, Integer> map = new HashMap<>();happiness = new int[N];flag = new boolean[N];costs = new int[N][N];String[] names = new String[N];names[0] = s.next();map.put(names[0], 0);for (int i=1; i<N; i++) {names[i] = s.next();map.put(names[i], i);happiness[i] = s.nextInt();}for (int i=0; i<N; i++) {for (int j=0; j<N; j++) {if(i == j) costs[i][j] = 0;else costs[i][j] = Integer.MAX_VALUE;}}for (int i=0; i<K; i++) {int m = map.get(s.next()), n = map.get(s.next());int cost = s.nextInt();costs[m][n] = cost;costs[n][m] = cost;}s.close();int des = map.get("ROM");flag[0] = true;List<Integer> temp = new ArrayList<>();temp.add(0);dfs(0, des, temp, 0);System.out.println(nums + " " + minCost + " " + maxJoy + " " + avergeJoy);for (int i : path) if (i == 0) System.out.print(names[0]);else System.out.print("->" + names[i]);}private static void dfs(int start, int des, List<Integer> temp, int cost) {if(start == des) {int joys = countJoy(temp);int tempAverge = joys / (temp.size() - 1);if (path == null || cost < minCost) {minCost = cost;path = new ArrayList<>(temp);maxJoy = joys;avergeJoy = tempAverge;nums = 1;} else if (cost == minCost) {nums ++;if (joys > maxJoy || (joys == maxJoy && tempAverge > avergeJoy)) {minCost = cost;path = new ArrayList<>(temp);maxJoy = joys;avergeJoy = tempAverge;}}return;}for (int i=0; i<N; i++) {if(i == start || flag[i] || costs[start][i] == Integer.MAX_VALUE) continue;flag[i] = true;temp.add(i);dfs(i, des, temp, cost+costs[start][i]);temp.remove(temp.size()-1);flag[i] = false;}}private static int countJoy(List<Integer> temp) {int sum = 0;for (int i : temp)sum += happiness[i];return sum;}
}
Dijkstra解法
package PAT;import java.util.*;public class Main {
int VerNum; //所有顶点个数int EleNum; //所有边的数目int des; //目的地,ROM节点编号HashMap<String, Integer> map = new HashMap<>(); //用来根据名称获取顶点序号的mapString[] stringList; //依据序号获取节点名称int[] pointCost; //存放从出发点到第i个节点的路径长度int[][] cost; //邻接矩阵存放边的信息int[] prev; //存放每个节点加入最短路径时的前一个顶点编号boolean[] used; //是否第i个节点已经在最短路径树了int byNum[]; //计算从起点到该点的经过顶点数目int totalhapp[]; //计算从出发点到第i个顶点时中最短路径的欢乐值int happ[]; //存放每一个顶点的欢乐值int route[]; //存放从出发点到第i个顶点的最短路径数目public Main(int v,int e){VerNum = v;EleNum = e;stringList = new String[v];pointCost =new int[v];prev = new int[v];used = new boolean[v];byNum = new int[v];totalhapp = new int[v];happ = new int[v];cost = new int[v][v];route=new int[v];for(int i =0;i<v;i++){pointCost[i] = 100000; //初始化为很大的一个值prev[i] = -1; //used[i] = false;byNum[i]=400; //因为顶点数不超过200totalhapp[i] = 0; //总欢乐值}}public void dijkstra(int s, int r){totalhapp[s] = happ[s];byNum[s]= 1;pointCost[s] = 0;route[s] = 1;for(int j =0;j<VerNum;j++){int v = -1;for(int i =0;i<VerNum;i++){if(!used[i] && (v==-1 || pointCost[i] < pointCost[v])){v = i;}}if(v == des) break;used[v] = true;for(int i=0;i<VerNum;i++){if(cost[v][i]!=0){if(pointCost[i] > pointCost[v] + cost[v][i]){pointCost[i]= pointCost[v] + cost[v][i];totalhapp[i] = totalhapp[v]+happ[i];byNum[i] = byNum[v] + 1;route[i] = route[v];prev[i] = v;}else if(pointCost[i] == pointCost[v]+cost[v][i]){route[i] = route[v]+route[i];if(totalhapp[i] < totalhapp[v]+happ[i]){totalhapp[i] = totalhapp[v] + happ[i];byNum[i] = byNum[v]+1;prev[i]= v;}else if (totalhapp[i] == totalhapp[v] +happ[i] && byNum[i]>byNum[v]+1){byNum[i] = byNum[v]+1;prev[i]= v;}}}}}}public static void main(String[] args){Scanner sc = new Scanner(System.in);Main rom = new Main(sc.nextInt(), sc.nextInt());rom.stringList[0] = sc.next();rom.map.put(rom.stringList[0], 0);for(int i =1; i<rom.VerNum; i++){rom.stringList[i] = sc.next();rom.map.put(rom.stringList[i], i);rom.happ[i] = sc.nextInt();}for(int i=0; i<rom.EleNum; i++){int from = rom.map.get(sc.next());int to = rom.map.get(sc.next());int val = sc.nextInt();rom.cost[from][to] = val;rom.cost[to][from] = val;}sc.close();int r = rom.map.get("ROM");rom.des = r;rom.dijkstra(0, r);System.out.println(rom.route[r]+" "+rom.pointCost[r]+" "+rom.totalhapp[r]+" "+rom.totalhapp[r]/(rom.byNum[r]-1));List<String> list = new ArrayList<>();while(true){if(r==-1)break;list.add(rom.stringList[r]);r = rom.prev[r];}for(int i =list.size()-1;i>=0;i--){System.out.print(list.get(i));if(i!=0){System.out.print("->");}}}
}
总结
对于最短路径问题,首先应该想到的是Dijkstra算法,然后注意为了省时,能不递归的就不用,以及scanner用好了可以省不少时间。