您的位置:首页 > 其它

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

2016-03-09 22:05 232 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: