Java 解决一张图使用了回溯剪枝算法 以及Java编写过程中的几点经验总结

/*
 * 
 * 时间最短的基础上,花费最少
 * */
package cn.java.mhz;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 * dfs search
 * @author MHZ
 *
 */

class DfsSearch{
	static final int maxv = 100+5;
	static final int INF = 0x3f3f3f3f;
	static final double INF1 = 1000000000.0;
	public Integer path[][];
	public boolean done[];
	public Integer parent[];
	public int solve;
	public double solvecost;
	public Integer path1[][];
	public Integer parent1[];
	public boolean flag;
	
	//构造方法
	DfsSearch(){
		this.flag=false;
		this.solve = INF;
		this.solvecost =INF1;
		this.path1=new Integer[maxv][];
		this.parent1=new Integer[maxv];
		this.path =new Integer[maxv][];
		for(int i = 0;i<maxv;i++)
		{
			path[i] = new Integer[maxv];
			for(int j=0;j<maxv;j++)
				path[i][j] = 0;
		}
		for(int i = 0;i<maxv;i++)
		{
			path1[i] = new Integer[maxv];
			for(int j=0;j<maxv;j++)
				path1[i][j] = 0;
		}
		this.parent =new Integer[maxv];
		this.done=new boolean[maxv];
		
		for(int i=1;i<maxv;i++)
			done[i]=false;
		for(int i = 0;i<maxv;i++)
		{
			parent[i] = 0;
		}
		for(int i = 0; i<maxv;i++)
		{
			parent1[i] = 0;
		}
	}
	
	//成员方法
	//相对于dfs,类中的其它均为全局变量
	public void dfs(Edge[][] G, int head,int end,int st,int en,int v,int pos,int sum,double sumcost)
	{
		if(sum > solve||(sum==solve&&sumcost>solvecost)) {
			return;
		}
		if(pos == end) {
			flag = true;
			if(solve>=sum) {
				if(solve>sum) {
					solve=sum;
					solvecost = sumcost;
					int last = end,cnt=0;
					while(last!=head)
					{
						parent1[cnt++] = last;
						last = parent[last];
					}
					parent1[cnt++] = head;
				
					for(int i=cnt-1;i-1>=0;i--)
						path1[parent1[i]][parent1[i-1]] = path[parent1[i]][parent1[i-1]];
				}
				else if(solve==sum) {
					if(solvecost > sumcost) {
						solvecost = sumcost;
						int last = end,cnt=0;
						while(last!=head)
						{
							parent1[cnt++] = last;
							last = parent[last];
						}
						parent1[cnt++] = head;
					
						for(int i=cnt-1;i-1>=0;i--)
							path1[parent1[i]][parent1[i-1]] = path[parent1[i]][parent1[i-1]];
					}
				}
			}
			return;
		}
		for(int i=1;i<=v;i++)
		{
			if(done[i]||G[pos][i].trans==null) {
				continue;
			}
			for(int j=0;j<G[pos][i].trans.size();j++)
			{
				if(pos==head) {
					if(G[pos][i].starttime.get(j)>=st&&G[pos][i].starttime.get(j)<=en) {
						parent[i] = pos;
						path[pos][i] = j;
						done[i] = true;
						dfs(G, head, end, st, G[pos][i].starttime.get(j)+G[pos][i].pasttime.get(j), v,i ,sum+G[pos][i].pasttime.get(j),sumcost+G[pos][i].cost.get(j));
						done[i] = false;
					}
				}
				else {
					//System.out.println(pos+" "+en +" "+ i +" "+G[pos][i].starttime.get(j));
					if(G[pos][i].starttime.get(j)>=en)
					{
						parent[i] = pos;
						path[pos][i] = j;
						done[i] = true;
						dfs(G, head, end, st, G[pos][i].starttime.get(j)+G[pos][i].pasttime.get(j), v, i ,sum+G[pos][i].pasttime.get(j)+G[pos][i].starttime.get(j)-en,sumcost+G[pos][i].cost.get(j));
						done[i] =false;
					}
				}
			}
		}
		done[pos]=false;
		return;
	}
}

class Edge{
		
		public ArrayList<Integer> starttime;
		public ArrayList<Integer> endtime; 
		public ArrayList<Double> cost;
		public ArrayList<Integer> pasttime;
		public ArrayList<String> trans;
		
		public Edge()
		{
			this.starttime = new ArrayList <Integer> ();
			this.endtime = new ArrayList<Integer> ();
			this.cost = new ArrayList<Double> ();
			this.pasttime = new ArrayList<Integer> ();
			this.trans = new ArrayList<String> ();
		}
		
		//构造器
		public Edge(ArrayList<Integer> starttime, ArrayList<Integer> endtime, ArrayList<Double> cost,
				ArrayList<Integer> pasttime, ArrayList<String> trans) {
			super();
			this.starttime = starttime;
			this.endtime = endtime;
			this.cost = cost;
			this.pasttime = pasttime;
			this.trans = trans;
		}
	}

public class MySearchClass {
	
	static final int maxv = 100+5;
	
	public static int time(String s)
	{
		int hour,minute;
		hour = (s.charAt(0)-'0')*10+(s.charAt(1)-'0');
		minute = (s.charAt(3)-'0')*10+(s.charAt(4)-'0');
		return hour*3600+minute*60;
	}
	
	//传入整张图,头结点,尾节点,可接受时间范围
	//寻找一条时间最短的途径
	public static void main(String[] args)
	{
		System.out.println("请输入点数和边数:");
		Integer v,e;
		Scanner sc = new Scanner(System.in);
		String str = new String();
		str = sc.nextLine();
		String[] str1 = str.trim().split(" ");
		v = Integer.parseInt(str1[0]);
		e = Integer.parseInt(str1[1]);
		System.out.println("请输入可接受出发时间区间:");
		String s = new String();
		s = sc.nextLine();
		str1 = s.trim().split(" ");
		//得到可接受时间区间
		Integer st = Integer.valueOf(time(str1[0]));
		Integer en = Integer.valueOf(time(str1[1]));
		//得到起始和终点
		System.out.println("请输入起始点和终点:");
		String s1 = new String();
		s1 = sc.nextLine();
		str1 = s1.trim().split(" ");
		
		Map<String,Integer> map1 = new HashMap<>();
		Map<Integer,String> map2 = new HashMap<>();
		map1.clear();
		map2.clear();
		int pos=1;
		System.out.println("请输入 边的信息:");
		Edge[][] G = new Edge[maxv][];
		for(int i=0;i<maxv;i++)
		{
			G[i] = new Edge[maxv];
			for(int j=0;j<maxv;j++)
			{
				G[i][j]=new Edge();
			}
		}
		//存放整个图结构
		for(int i=0;i<e;i++)
		{
			String s2=sc.nextLine();
			String[] str11 = s2.trim().split(" ");
			
			int v1,v2;
			if(map1.containsKey(str11[0])) {
				v1 = map1.get(str11[0]);
			}
			else {
				v1 = pos;
				map1.put(str11[0], pos);
				map2.put(pos, str11[0]);
				pos++;
			}
			
			if(map1.containsKey(str11[1]))
			{
				v2 = map1.get(str11[1]);
			}
			else {
				v2 = pos;
				map1.put(str11[1],pos);
				map2.put(pos, str11[1]);
				pos++;
			}
			
			//有向图
			G[v1][v2].trans.add((String)str11[2]);
			G[v1][v2].starttime.add(time(str11[3]));
			G[v1][v2].endtime.add(time(str11[4]));
			G[v1][v2].cost.add(Double.parseDouble(str11[5]));
			G[v1][v2].pasttime.add(Integer.parseInt(str11[6]));
			
//			System.out.printf("%d %d\n",v1,v2);
			
		}
		DfsSearch Ms = new DfsSearch();
		int head = map1.get(str1[0]);
		int end = map1.get(str1[1]);
		Ms.done[head]=true;
		Ms.flag=false;
		Ms.dfs(G, head, end, st, en, v, head, 0,0.0);
		if(Ms.flag==true)
		{
			Integer []parent = Ms.parent1;
			Integer [][]path = Ms.path1;
			List <Integer> L = new ArrayList<>();
			for(int i=0;i<parent.length;i++)
				if(parent[i].equals(head)) break;
				else {
					L.add(parent[i]);
				}
			L.add(head);
		
			//数组翻转
			int len = L.size();
			for(int i=0;i<len/2;i++)
			{
				parent[i] = parent[len-1-i] + parent[i];
				parent[len-1-i] = parent[i] - parent[len-1-i];
				parent[i] = parent[i] - parent[len-1-i];
			}
			StringBuilder sb =new StringBuilder();
			System.out.printf("由%s到达%s花费时间最短的基础上,金钱花费最少的乘坐方式为:\n",str1[0],str1[1]);
			sb.append("   ");
			for(int i=0;i<len-1;i++)
			{
				if(i==0) {
					sb.append(map2.get(parent[i])+"--"+G[parent[i]][parent[i+1]].trans.get(path[parent[i]][parent[i+1]])+"--"+map2.get(parent[i+1]));
				}
				else {
					sb.append("--"+G[parent[i]][parent[i+1]].trans.get(path[parent[i]][parent[i+1]])+"--"+map2.get(parent[i+1]));
				}
				if(i==len-2) {
					break;
				}
			}
			System.out.println(sb.toString());
			//System.out.println(Ms.solve);
			//System.out.println(Ms.solvecost);
		}else {
			System.out.println("在指定的时间区间内未查询到可出行的路径!!!!");
		}
		
	}
}

几点经验总结:

1.java中为了安全与封装,避免使用全局变量,但是有时候需要使用全局变量,这种时候可以另外定义一个类,把所要使用的方法定义为类的成员方法,这样相对于类的成员方法,类的成员变量便成为了这个类的全局变量,再实例化类的对象,通过类的对象来调用此方法即可

2.写这个代码的时候,经常遇到空指针的问题,后来才发现是由于二维数组在初始化的时候,要对二维数组中的每一个元素都进行初始化,而不是仅仅对于整个数组进行初始化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯算法是一种通过不断地尝试所有可能的解决方案来解决问题的算法。它通常用于解决组合、排列、子集、棋盘等问题。回溯算法的基本思想是在每一步尝试所有可能的解决方案,如果当前方案无法解决问题,则回溯到上一步并尝试其他方案,直到找到解决方案或者所有可能的方案都被尝试过。 剪枝操作是回溯算法的一种优化技巧,它可以减少搜索空间,从而提高算法的效率。剪枝操作通常在搜索过程进行,通过判断当前搜索路径是否有可能找到解决方案,来决定是否继续搜索。常见的剪枝操作包括:去重剪枝、可行性剪枝、最优性剪枝等。 下面是Java实现回溯算法及其剪枝操作的代码示例: 1. 未优化的回溯算法代码: ``` class Solution { List<List<Integer>> lists = new ArrayList<>(); List<Integer> path = new ArrayList<>(); public List<List<Integer>> combine(int n, int k) { backTracking(n, k, 1); return lists; } public void backTracking(int n, int k, int startIndex){ if(k == path.size()){ lists.add(new ArrayList<>(path)); return; } for(int i = startIndex; i <= n; i++){ path.add(i); backTracking(n, k, i + 1); path.remove(path.size() - 1); } } } ``` 2. 去重剪枝优化的回溯算法代码: ``` class Solution { List<List<Integer>> lists = new ArrayList<>(); List<Integer> path = new ArrayList<>(); public List<List<Integer>> combine(int n, int k) { backTracking(n, k, 1); return lists; } public void backTracking(int n, int k, int startIndex){ if(k == path.size()){ lists.add(new ArrayList<>(path)); return; } for(int i = startIndex; i <= n - (k - path.size()) + 1; i++){ path.add(i); backTracking(n, k, i + 1); path.remove(path.size() - 1); } } } ``` 在上面的代码,我们使用了去重剪枝来避免重复搜索相同的解决方案。具体来说,我们在搜索过程,限制了每个数字只能出现一次,从而避免了重复搜索相同的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值