[2016/03/09] 关于深搜的题目整理和思路 & 蓝桥杯历年试题 - 大臣的路费/颠倒的价牌

7 篇文章 0 订阅

0 一些废话

说废话已经是我的习惯了。那么关于深搜,老早就想整理了,这毕竟是小白我唯一会的算法嘛。

同样,网上基本找不到Java的深搜样本,当然,那是因为算法不需要用java写。不过,作为总结和整理,我还是用java写一份吧=-=不过跟C也没什么区别。


1 经典题目:走迷宫

package Basic;

import java.util.Scanner;

/**深搜走迷宫算法
 * 判断是否能从迷宫的入口到达出口
输入: 先输入两个整数表示迷宫的行数m和列数n,
再输入口和出口的坐标,最后分m行输入迷宫,
其中1表示墙,0表示空格每个数字之间都有空格。
输出: 若能到达,则输出"Yes",否则输出"No",结果占一行。
 * 16/02/29
 * @author Moplast
 */

public class Main {
	//为了方便函数访问它,所以定义为类内全局变量
	static int num=0;
	static int minnum=0;
	static int Rownum,Colnum;
	static int Beginrow,Begincol,Endrow,Endcol;
	static int state;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		while(sc.hasNext()){
			Rownum=sc.nextInt();//迷宫行数
			Colnum=sc.nextInt();//迷宫列数
			Beginrow=sc.nextInt();Begincol=sc.nextInt();//起点坐标
			Endrow=sc.nextInt();Endcol=sc.nextInt();//终点坐标
			state=0;//迷宫走通与否状态
			int[][] Arr = new int[Rownum][Colnum];//存储迷宫
			
			//将输入数据读入迷宫数组
			for(int i=0;i<Rownum;i++){
				for(int j=0;j<Colnum;j++){
					Arr[i][j]=sc.nextInt();
				}
			}
			
			//开始走迷宫,标记开始走的一步(以及之后走的地方)为1(墙)
			//即不再回头走(回头走也没有意义的)
			Arr[Beginrow][Begincol]=1;
			//在这一步上查找下一个可以前进的地方,即开始归溯
			//这里为了不让数组变成全局变量(不太好初始化)
			//所以把数组作为参数传进去了
			search(Beginrow,Begincol,Arr);
			//一个迷宫搜索路径后的最终状态,起始点接通与否
			int step_num=Arr[Endrow][Endcol]-1;
			
			/*第一个问题:是否可以走通迷宫?*/
			if(state==1){
				System.out.println("1: Yes");
			}else{
				System.out.println("1: No");
			}
			
			/*第二个问题:共有多少种不重复的走法?*/
			if(state==1){
				System.out.println("2: "+num);
			}
		}
	}

	private static void search(int row, int col, int[][] Arr) {
		//第一步判断,是否当前已经走到终点,如果是,更新走通状态,返回
		if(row==Endrow && col==Endcol){
			state=1;
			num++;
		}
		//r-new row,c-new col
		int r,c;
		
		Arr[row][col]=1;//对可以走通的点进行标记
		
		//接下来开始上下左右尝试走了
		
		//上
		r=row-1;c=col;
		//如果路可以走通,则继续开始找新路
		if(canplace(r,c,Arr)) {
			Arr[r][c]=num+1;
			search(r,c,Arr);//深搜定义体现出来了,一条道走到黑就是这样~
		}
		//下
		r=row+1;c=col;
		if(canplace(r,c,Arr)){
			Arr[r][c]=num+1;
			search(r,c, Arr);
		}
		
		//左
		r=row;c=col-1;
		if(canplace(r,c,Arr)){
			Arr[r][c]=num+1;
			search(r,c, Arr);
		}			
		
		//右
		r=row;c=col+1;
		if(canplace(r,c,Arr)){
			Arr[r][c]=num+1;			
			search(r,c, Arr);
		}
	}

	/*本函数用于判断Arr(r,c)是否可以访问*/
	private static boolean canplace(int row, int col, int[][] Arr) {
		//首先,不能越界,四个条件
		if(row>=0 && col >=0 && row<Rownum && col<Colnum){
			//其次,该处可以通行
			if(Arr[row][col]==0)
				return true;			
		}		
		return false;
	}
}

废话不多说了,因为我的注释里全是废话。


2 蓝桥杯试题:13种*4扑克牌取13张

package Basic;
/**
 * 深搜:13种扑克牌,每种扑克牌有4张。从中取13张,共有多少种?
 * 【深搜的基本思路】
 * main()函数中:先确定第一步,进行初始化。初始化数组、方向数组(如果有)、起始点位置等。
 * 然后进行search()
 * 
 * search()函数中
 * 加上方向增量,形成新的坐标。
 * 循环内:
 * ① 先判断(是否出界,是否已经走过,是否……)
 * ② 给值
 * ③ 走向新的坐标
 * ④ 判断是否到了终点
 * ⑤ 如果没有,则search下一层
 * ⑥ 回溯回来,则回复未走标志(因为如果路能走通,是不会回来的)
 * @author Android
 *
 */
public class DFS {
	//全局变量	
	static int[] save=new int[14];//拿的13张牌
	static int[] rest=new int[14];//13种牌的对应剩下数量
	static int num=0;
	public static void main(String[] args) {
		for(int i=1;i<14;i++){
			rest[i]=4;
		}
		search(1);
		
		System.out.println(num);
	}

	private static void search(int x) {
		// TODO Auto-generated method stub
		for(int i=1;i<=13;i++){
			if(check(i, x)){      //①先判断(是否出界,是否已经走过,是否……)
				save[x]=i;	//② 给值 ③ 走向新的坐标
				if(x==13)	//④ 判断是否到了终点
					output();
				else{
					rest[i]--;	
					search(x+1);	//⑤ 如果没有,则search下一层
					rest[i]++;	//⑥ 回溯回来,则回复未走标志(因为如果路能走通,是不会回来的)
					save[x]=0;
				}
			}
		}
	}

	private static boolean check(int i,int x) {
		if(rest[i]>0 && i>=save[x-1]) //i>=save[x-1]是必要的剪枝操作,保证后一个数大于等于前一个数
			return true;
		return false;
	}

	private static void output() {
		num++;		
	}
}
同样,废话一堆堆。


3 环排列:珠串问题

留个白,我还没搞懂。


4 蓝桥杯试题:大臣的路费

import java.util.Scanner;

public class Main {
	static int n;//n个城市
	static boolean[] flag=new boolean[n+1];//记录城市n是否走过
	static int[][] map=new int[n+1][n+1];//记录高速公路
	static int beginCity,endCity;//记录开始城市,结束城市
	static int kmNum=0;//千米数
	static int maxkmNum=0;//最大千米数
	public static void main(String[] args) {
		Scanner sc=new Scanner (System.in);
		while(sc.hasNext()){
			n=sc.nextInt();
			//这里如果不重新实例化的话,n默认是0
			flag=new boolean[n+1];
			map=new int[n+1][n+1];
			//注意如果有路的话,要储存双向的路径
			for(int i=0;i<n-1;i++){
				int p=sc.nextInt();
				int q=sc.nextInt();
				map[p][q]=sc.nextInt();
				map[q][p]=map[p][q];
			}
			//从城市1开始深搜
			for(int i=1;i<=n;i++){
				beginCity=i;
				flag[i]=true;
				search(i);
				flag[i]=false;//这句比较关键,回溯回来要把记录清除
			}
			
			//知道最大千米数就好办了,叠加即可
			int sum=0;
			for(int i=11;i<maxkmNum+11;i++){
				sum+=i;
			}
			System.out.println(sum);
		}
	}
	
	private static void search(int i) {
		//这里k还是得从1开始,否则会漏掉一些情况
		for(int k=1;k<=n;k++){
			//检查是否可走
			if(check(i,k)){
				//可以走的话,就走出这一步,加上千米数,并标记走过
				kmNum+=map[i][k];
				flag[k]=true;
				//深搜下一个城市
				search(k);
				
				//回溯还原之前的操作
				kmNum-=map[i][k];
				flag[k]=false;
			}
			//如果找不到路,且不是原地打转
			else if(!check(i,k) && k==n && i!=beginCity){
				//记录结束城市
				endCity=i;
				//记录最大千米数
				if(kmNum>maxkmNum)
					maxkmNum=kmNum;
				//还原记录
				flag[i]=false;
				//output();
				return;
			}
		}

	}
	private static void output() {
		// TODO Auto-generated method stub
		System.out.print("BeginCity:"+beginCity);
		System.out.print(" EndCity:"+endCity);
		System.out.println(" km:"+kmNum);		
	}
	private static boolean check(int i,int j) {
		//三个条件:1. 不是一个城市到自身; 2.i,j之间有通路; 3. 城市j未走过
		if(j!=i && map[i][j]>0 && !flag[j])
			return true;
		return false;
	}

}
只能拿到3/4的分数。最后一个测试点超时。

我反思一下程序的问题,就是冗余度太高。在之后的遍历中,有的时候不需要往前遍历,因为之前已经算过了。所以其实应该开辟一个存储空间来存储以前的结果,或者说,在输出结果中,应该是BeginCity<EndCity的组合,而程序等于来回反复做了两遍,不超时才怪。


5 蓝桥杯试题:颠倒的价牌

编程序无能,但是这道题挺有意思,所以就用做趣味数学题的方法暴力地蒙了出来。


可用的数是0 1 2 5 6 8 9

而显然有算式

   2 _ _

+ 5 5 8

————

= 8 _ _


而二百多和八百多都不超过一千,说明颠倒以后,第一个数(千位)和最后一个数(各位)相差不超过1。这么一想只有以下几种组合:

1> 1和2,产生末位9 即 XX9/XXX9

2> 2和5 产生末位0

3> 6和9 产生末位0

4> 8和6 产生末位2

5> 5和9 产生末位6


把这些末位整理一下,发现二百多的末位肯定是2,而八百多的末位肯定是0。否则不能满足算式。

所以亏两百多的价格肯定是 9XX8,而倒过来的价格是8XX6。好,接下来就凑吧,记得凑出来以后带回八百多的价格的时候也要正确才可以。


答案应该是9088,此时赚800多的应该是X90X,X可以为2/5,也可以为6/9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值