201712-4 行车路线

题目:

试题名称:行车路线

时间限制:1.0s

内存限制:256.0MB

问题描述
  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走 s公里小明会增加 s 2的疲劳度。
  例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2) 2+2+2 2=16+2+4=22。
  现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
 
输入格式
  输入的第一行包含两个整数 nm,分别表示路口的数量和道路的数量。路口由1至 n编号,小明需要开车从1号路口到 n号路口。
  接下来 m行描述道路,每行包含四个整数 tabc,表示一条类型为 t,连接 ab两个路口,长度为 c公里的双向道路。其中 t为0表示大道, t为1表示小道。保证1号路口和 n号路口是连通的。
 
输出格式
  输出一个整数,表示最优路线下小明的疲劳度。
 
样例输入
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
样例输出
76
样例说明
  从1走小道到2,再走小道到3,疲劳度为5 2=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
  对于30%的评测用例,1 ≤  n ≤ 8,1 ≤  m ≤ 10;
  对于另外20%的评测用例,不存在小道;
  对于另外20%的评测用例,所有的小道不相交;
  对于所有评测用例,1 ≤  n ≤ 500,1 ≤  m ≤ 10 5,1 ≤  ab ≤  nt是0或1, c  ≤ 10 5。保证答案不超过10 6

 

 

解法1:暴力DFS

思路:搜索所有可能的路径,更新最小值。

结果:超时(30分)

代码:

import java.util.Scanner;

public class Main{
	final int Max_N = 500;
	int n;
	long shortest = -1;
	Edge[][] edge = new Edge[Max_N][Max_N];
	boolean[] isInP = new boolean[Max_N]; //记录路径
	void run(){
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		int m = in.nextInt();
		in.nextLine();
		for(int i = 0;i < m; i++){
			int t = in.nextInt();
			int a = in.nextInt();
			int b = in.nextInt();
			int c = in.nextInt();
			in.nextLine();
			edge[a-1][b-1] = edge[b-1][a-1] = new Edge(t,c);
		}
		in.close();
		DFS(0,new long[2]);
		System.out.println(shortest);
	}
	
	void DFS(int index,long[] dis){
		if(n - 1 == index){
			if(shortest == -1 || dis[0] < shortest)
				shortest = dis[0];
			return ;
		}
		long[] lastdis = new long[2];
		lastdis[0] = dis[0];lastdis[1] = dis[1];
		for(int i = 0; i < n; i++){
			if(!isInP[i] && edge[index][i] != null){
				long w = edge[index][i].weight;
				if(edge[index][i].type == 1){
					//(x1 + x2)^2 = x1^2 + 2*x1*x2 + x2^2
					//(x1 + x2 + x3)^2 = (x1 + x2)^2 + 2*(x1 + x2)*x3 +x3^2
					dis[0] += (w*w + 2*dis[1]*w);
					dis[1] += w;
				}else{
					dis[0] += w;
					dis[1] = 0;
				}
				if(shortest ==-1 || dis[0] < shortest) { //小优化
					isInP[i] = true;
					DFS(i,dis);
					isInP[i] = false;
				}
				dis[0] = lastdis[0]; dis[1] = lastdis[1];
			}
		}
		return ;
	}
	public static void main(String[] args) {
		new Main().run();
	}
}
class Edge{
	public int type;
	public long weight;
	public Edge(int t, long w){
		this.type = t;
		this.weight = w;
	}
}

  

解法2:

思路:使用dijkstra算法求解最短路径,每次走小路时记录小路的总长。

结果:错误(90分),错误的原因是小路的存在导致得到的解只是次优。

代码:

import java.util.Scanner;

public class Main{
	final int Max_N = 500;
	final long INF = (long) 1e9;
	long shortest = -1;
	int n;
	Edge[][] edge = new Edge[Max_N][Max_N];
	void run(){
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		int m = in.nextInt();
		in.nextLine();
		for(int i = 0;i < m; i++){
			int t = in.nextInt();
			int a = in.nextInt();
			int b = in.nextInt();
			int c = in.nextInt();
			in.nextLine();
			edge[a-1][b-1] = edge[b-1][a-1] = new Edge(t,c);
		}
		in.close();
		dijkstra(0);
		System.out.println(shortest);
	}
	void dijkstra(int start){
		long[][] dis = new long[n][2];
		boolean[] isVisit = new boolean[n];
		for(int i = 0;i < n; i++){
			if(edge[start][i] != null){//有路
				long w = edge[start][i].weight;
				if(edge[start][i].type == 1){//小路。
					dis[i][0] = w * w; //疲劳值。
					dis[i][1] = w;     //小路的总长度。
				}else {
					dis[i][0] = w;
					dis[i][1] = 0;
				}
			}else{//无路
				dis[i][0] = INF;
				dis[i][1] = 0;
			}
		}
		dis[start][0] = 0;
		dis[start][1] = 0;
		isVisit[start] = true;

		for(int i = 0; i < n; i++){
			int u = -1;long min = INF;
			for(int j = 0; j < n; j++){
				if(!isVisit[j] && dis[j][0] < min){
					min = dis[j][0];
					u = j;
				}
			}
			if(u == -1 || u == n - 1){
				shortest = dis[ n - 1][0];
				return ;
			}
			isVisit[u] = true;
			
			for(int j = 0; j < n; j++){
				if(!isVisit[j] && edge[u][j] != null ){ //优化未走过的点
					long w = edge[u][j].weight;
					if(edge[u][j].type == 0){ //走大路过去
						if(w + dis[u][0] < dis[j][0]){
							dis[j][0] = dis[u][0] + w;
							dis[j][1] = 0;
						}
					}else {//走小路过去
						if(w*w + 2*w*dis[u][1] + dis[u][0] < dis[j][0]){
							//(x1 + x2)^2 = x1^2 + 2*x1*x2 + x2^2
							//(x1 + x2 + x3)^2 = (x1 + x2)^2 + 2*(x1 + x2)*x3 +x3^2
							dis[j][0] = w*w + 2*w*dis[u][1] + dis[u][0];
							dis[j][1] = dis[u][0] + w;
						}
					}
				}
			}
		}
	}
	public static void main(String[] args) {
		new Main().run();
	}
}
class Edge{
	public int type;
	public long weight;
	public Edge(int t, long w){
		this.type = t;
		this.weight = w;
	}
}

解法3 :

思路:把小路与大路分开,用两个图表示。分别用最短路径算法求解。

结果:错误(60分)。但是此种解法是正解,网上使用Floyd + Spfa得了100分。

代码:

import java.util.Scanner;
import static java.lang.Math.*;
public class Main{
	final int Max_N = 505;
	final long INF = (long)1e9;
	int n;
	long shortest = -1;
	long[][] low = new long[Max_N][Max_N];
	long[][] high = new long[Max_N][Max_N];
	void run(){
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		int m = in.nextInt();
		in.nextLine();
		//初始化
		for(int i = 0; i < n; i++){
			for(int j = 0; j < n;j++){
				low[i][j] = low[j][i] = (long)1e9;
				high[i][j] = high[j][i] = (long)1e9;
			}
			low[i][i] = high[i][i] = 0;
		}

		for(int i = 0;i < m; i++){
			int t = in.nextInt();
			int a = in.nextInt();
			int b = in.nextInt();
			int c = in.nextInt();
			in.nextLine();
			if(t == 1){
				if(low[a - 1][b - 1] > c) 
					low[a-1][b-1] = low[b-1][a-1] = c;
			}else{
				if(high[a - 1][b - 1] > c)
					high[a-1][b-1] = high[b-1][a-1] = c;
			}
		}
		in.close();
		floyd();
		dijkstra(0);
		System.out.println(shortest);
	}
	void dijkstra(int s){
		long[] d_low = new long[n];
		long[] d_high = new long[n];
		for(int i = 0;i < n; i++){ // 分别初始化距离数组。
			if(low[s][i] < INF){
				d_low[i] = low[s][i] * low[s][i];
			}else{
				d_low[i] = low[s][i];
			}			
			d_high[i] = high[s][i];
		}
		boolean[] isVisit = new boolean[n];
		isVisit[s] = true;
		for(int i = 0; i < n; i++){
			int u = -1;long m = INF;
			for(int j = 0; j < n; j++){//找下一个优化的点。
				if(!isVisit[j] && d_high[j] < m){
					m = d_high[j];
					u = j;
				 }
			}
			if(u == -1 || u == n - 1) {
				shortest = min(d_low[n - 1], d_high[n - 1]);
				return ;
			}
			isVisit[u] = true;
			m = min(d_low[u], d_high[u]); //走小路到这里更近吗?
			for(int j = 0; j < n; j++){//更新邻接点。
				if(!isVisit[j] && m + high[u][j] < d_high[j]){
					d_high[j] = m + high[u][j];
				}
				if(!isVisit[j] && low[u][j] < INF 
				&& d_high[u] + low[u][j] * low[u][j] < d_low[j]){//走大道更近吗?
						d_low[j] = d_high[u] + low[u][j] * low[u][j];
				}
			}
		}
	}
	void floyd(){
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++)
				for(int j = 0; j <n; j++)
					low[i][j] = min(low[i][j], low[i][k] + low[k][j]);
	}
	public static void main(String[] args) {
		new Main().run();
	}
}

 上述代码参考其他人的代码,存在部分未知错误。

正解: https://blog.csdn.net/qq_36172505/article/details/81324997

转载于:https://www.cnblogs.com/suen061/p/11384293.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值