【做题分享】大臣的旅费、九宫重排 2021-04-08

1.大臣的旅费

1)题目 :

深度优先搜索 邻接表

问题描述
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。

输出格式
输出一个整数,表示大臣J最多花费的路费是多少。

2)题解:

方法一: 常规dfs

import java.util.*;

public class _大臣的旅费 {
	private static int[][] distances;
	private static boolean[] hashtable;
	private static int n;
	private static int sum_dis=0;
	private static int fee=0;
	private static int fee_max=0;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		distances = new int[n][n];
		hashtable = new boolean[n];
		int a1=0,a2=0,a3=0;
		for(int i=0;i<n-1;i++) {
			a1 = sc.nextInt();
			a2 = sc.nextInt();
			a3 = sc.nextInt();
			distances[a1-1][a2-1]=a3;
			distances[a2-1][a1-1]=a3;
		}
		for(int j=0;j<n;j++) {
			dfs(j);
		}
		System.out.println(fee_max);
	}
	
	private static void dfs(int current) {
		int fee_current=0;
		hashtable[current]=true;
		for(int j=0;j<n;j++) {
			if(distances[current][j]!=0) {
				if(hashtable[j]==false) {
					fee_current=distances[current][j]*(10+sum_dis)+(distances[current][j]+1)*distances[current][j]/2;
					fee+=fee_current;
					sum_dis+=distances[current][j];
					if(fee>fee_max) {
						fee_max=fee;
					}
					dfs(j);
					sum_dis-=distances[current][j];
					fee-=fee_current;				
				}
			}
		}
		hashtable[current]=false;
	}
}

可通过大部分(75%)测试用例,但是耗时、耗内存
在这里插入图片描述


方法二

因为任何一个大城市都能从首都直接或者通过其他大城市间接到达,所以将地图看成是树,根节点为首都,则题目所求的可以看成是树的直径,可用两次dfs(bfs)求解:第一次dfs找出离首都最远的城市fartest_node,第二次dfs以这个城市fartest_node为起点搜索

import java.util.*;

public class _大臣的旅费 {
	private static int[][] distances;
	private static boolean[] hashtable;
	private static int n;
	private static int sum_dis=0;
	private static int fee=0;
	private static int fee_max=0;
	private static int fartest_node=0;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		distances = new int[n][n];
		hashtable = new boolean[n];
		int a1=0,a2=0,a3=0;
		for(int i=0;i<n-1;i++) {
			a1 = sc.nextInt();
			a2 = sc.nextInt();
			a3 = sc.nextInt();
			distances[a1-1][a2-1]=a3;
			distances[a2-1][a1-1]=a3;
		}
		
		dfs(0);
		dfs(fartest_node);
		System.out.println(fee_max);
	}
	private static void dfs(int current) {
		int fee_current=0;
		hashtable[current]=true;
		for(int j=0;j<n;j++) {
			if(distances[current][j]!=0) {
				if(hashtable[j]==false) {
					fee_current=distances[current][j]*(10+sum_dis)+(distances[current][j]+1)*distances[current][j]/2;
						fee+=fee_current;
						sum_dis+=distances[current][j];
						if(fee>fee_max) {
							fee_max=fee;
							fartest_node=j;
						}
						dfs(j);
						sum_dis-=distances[current][j];
						fee-=fee_current;						
				}
			}
		}
		hashtable[current]=false;
	}
}

速度提升但是仍然超内存,因为仍然采用邻接矩阵存储数据,但是数据稀疏,造成空间浪费。
在这里插入图片描述
由于最后一个测试用例有10000个点,所以使用二维数组占用内存非常大。


方法三

邻接表存储结构,改善内存使用。

import java.util.*;

public class Main {

	private static boolean[] hashtable;
	private static int n;
	private static int sum_dis=0;
	private static int fee=0;
	private static int fee_max=0;
	private static int fartest_node=0;
	private static LinkedList<Edge>[] E1;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		
		//注意边集的初始化
		E1 = new LinkedList[n];
		for(int i=0;i<n;i++) {
			E1[i] = new LinkedList<Edge>();
		}
		
		hashtable = new boolean[n];
		for(int i=0;i<n-1;i++) {
			int V1 = sc.nextInt();
			int V2 = sc.nextInt();
			int D  = sc.nextInt();
			E1[V1-1].add(new Edge(V1-1,V2-1,D));
			E1[V2-1].add(new Edge(V2-1,V1-1,D));
		}
		
	
		dfs(0);
		System.out.println(fartest_node);
		dfs(fartest_node);
		System.out.println(fee_max);
		
	}
	//图的边
		private static class Edge{ //类应该用static修饰
		private int Vertex_1;
		private int Vertex_2;
		private int distance;
		
		public Edge(int Vertex_1,int Vertex_2 ,int distance) {  //该方法需要为public
			this.Vertex_1 = Vertex_1;
			this.Vertex_2 = Vertex_2;
			this.distance = distance;
		}
	}
	
	private static void dfs(int current) {
		int fee_current=0;
		hashtable[current]=true;
		for(int j=0;j<E1[current].size();j++) {
			int index = E1[current].get(j).Vertex_2;
			int distance = E1[current].get(j).distance;
			if(hashtable[index]==false) {
				fee_current=distance*(10+sum_dis)+(distance+1)*distance/2;
				fee+=fee_current;
				sum_dis+=distance;
				if(fee>fee_max) {
					fee_max=fee;
					fartest_node=index;
				}
				dfs(index);
				sum_dis-=distance;
				fee-=fee_current;//此处注意回退时,费用也要减掉
			}	
		}
		hashtable[current]=false;
	}
}

其中边集的初始化需要注意

E1 = new LinkedList[n];
for(int i=0;i<n;i++) {
	E1[i] = new LinkedList<Edge>();
}

在这里插入图片描述
内存占用明显减少


2.九宫重排

广度优先搜索 剪枝

1)题目

问题描述
  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
  在这里插入图片描述
  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入格式
  输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
  输出最少的步数,如果不存在方案,则输出-1。
样例输入:
12345678.
123.46758
样例输出
3

2)题解

方法一
BFS,一开始没有思考,直接用了字符串操作,字符串拼接比较耗时。因为如果是数组操作,每次移动只需要交换两个元素,而如果是字符串操作,每次移动至少需要拼接三次。

import java.util.*;
public class Main {

	static Set<String> visited = new HashSet<String>();
	static int steps = 0;
	static int min_steps = -1;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String origin = sc.nextLine();
		String target = sc.nextLine();
		
		
		bfs(origin,target);
		
		System.out.println(min_steps);
	}
	
	static void bfs(String start,String target) {
		Queue<String> queue = new LinkedList<String>();
		queue.offer(start);
		queue.offer(null);
		
		while(!queue.isEmpty()) {
			String cur = queue.poll();
			if(cur==null&&queue.peek()!=null) {
				steps+=1;
				
				queue.offer(null);
			}
			else if(cur.equals(target)) {  //注意要使用equals()方法判断字符串内容是否相同
				if(min_steps==-1||steps<min_steps) {
					min_steps=steps;
					return;
				}
			}
			else if(cur!=null) {
				visited.add(cur);
				int blank=cur.indexOf('.');
				if(blank>2) {//空格上移
					String subString = cur.substring(0, blank-3)+"."+cur.substring(blank-2,blank)+cur.substring(blank-3,blank-2)+cur.substring(blank+1);
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank<6) {//空格下移
					String subString = cur.substring(0,blank)+cur.substring(blank+3,blank+4)+cur.substring(blank+1,blank+3)+"."+cur.substring(blank+4);
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank%3>0) {//空格左移
					String subString = cur.substring(0,blank-1)+"."+cur.substring(blank-1,blank)+cur.substring(blank+1);
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank%3<2) {//空格右移
					String subString = cur.substring(0,blank)+cur.substring(blank+1,blank+2)+"."+cur.substring(blank+2); 
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
			}
			
		}
	}

}

在这里插入图片描述
优化成为字符数组操作,节省时间。

public class Main {

	static Set<String> visited = new HashSet<String>();
	static int steps = 0;
	static int min_steps = -1;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String origin = sc.nextLine();
		String target = sc.nextLine();
		
		
		bfs(origin,target);
		
		System.out.println(min_steps);
	}
	
	static void bfs(String start,String target) {
		Queue<String> queue = new LinkedList<String>();
		queue.offer(start);
		queue.offer(null);
		
		while(!queue.isEmpty()) {
			String cur = queue.poll();
			if(cur==null&&queue.peek()!=null) {
				steps+=1;
				queue.offer(null);
			}
			else if(cur.equals(target)) {
				if(min_steps==-1||steps<min_steps) {
					min_steps=steps;
					return;
				}
			}
			else if(cur!=null) {
				visited.add(cur);
				int blank=cur.indexOf('.');
				if(blank>2) {//空格上移
					char[] ch = cur.toCharArray();
					swap(blank,blank-3,ch);
					String subString = new String(ch);
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank<6) {//空格下移
					char[] ch = cur.toCharArray();
					
					swap(blank,blank+3,ch);
					String subString = new String(ch);
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank%3>0) {//空格左移
					char[] ch = cur.toCharArray();
					swap(blank,blank-1,ch);
					String subString = new String(ch); 
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
				if(blank%3<2) {//空格右移
					char[] ch = cur.toCharArray();
					swap(blank,blank+1,ch);
					String subString = new String(ch); 
					if(!visited.contains(subString)) {
						queue.offer(subString);
					}
				}
			}
			
		}
	}
	static void swap(int x,int y,char[] ch) {
		char temp = ch[x];
		ch[x] = ch[y];
		ch[y] = temp;
	}
}

在这里插入图片描述

注意

  • 区分ch.toString()和new String(ch)
  • 区分a.equals(b)和a==b
  • 明确字符数组操作和字符串操作优缺点

方法二
dfs和bfs都属于盲目的图搜索,A*搜索,属于启发式搜索的一种,暂未实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值