暴力搜索
暴力搜索是将所有的可能性罗列出来,在其中寻找答案的方法。
递归函数
一个函数中再次调用该函数自身的行为叫做递归
求n得阶乘
栈
后进先出
函数的调用的过程是通过使用栈实现的。因此,递归函数的递归过程也可以改用栈上的操作来实现。
现实当中需要如此改写的场合并不多,不过作为使用栈的练习试试看也不错的。
队列
先进先出
java 和c++中的函数的名称与用途有不同,要注意
深度优先搜索(DFS)
从a1开始按顺序决定每个数加或不加,在全部n个数都决定后再判断他们的和是不是K即可。因为状态数是2n+1,所以复杂度是O(2n)。
package 程序设计竞赛;
import java.util.Scanner;
public class 部分和问题 {
private static int k;
private static int n;
private static int[] a;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
n = sc.nextInt();
a = new int[n];
//a[0]=0;
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
k = sc.nextInt();
if(dfs(0,0))
System.out.println("Yes");
else
System.out.println("No");
}
//已经从前i项得到了和sum,然后对于i项之后的进行分支
public static boolean dfs(int i,int sum){
//如果前n项都计算过了,则返回sum是否与K相等
if(i==n)
return sum==k;
//不加上a[i]的情况
if(dfs(i+1,sum))
return true;
//加上a[i]的情况
if(dfs(i+1,sum+a[i]))
return true;
//无论是否加上a[i]都不能凑成k就返回false;
return false;
}
}
深度优先搜索是从最开始的状态出发,遍历所有可以到达的状态。由此可以对所有的状态进行操作或者列举出所有的状态
Lake Counting
知道图中不存在w为止,总共进行DFS的次数就是答案了。
8个方向共对应了8种状态转移,每个格子作为DFS的参数至多被调用1次,所以时间负责度位O(8NM)=O(N*M)
我把输入数据放在这里大家好对比
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
package 程序设计竞赛;
import java.util.Scanner;
public class 水洼问题 {
private static char[][] a;
private static int n;
private static int m;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
a = new char[n][m];
//输入
for(int i=0;i<n;i++){
String s=sc.next();
for(int j=0;j<s.length();j++){
a[i][j]=s.charAt(j);
}
}
int res=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(a[i][j]=='W'){
//从右w的地方开始深搜
dfs(i,j);
res++;
}
}
}
System.out.println(res);
}
//字符图的转换
public static void dfs(int x,int y){
//将现在的位置替换位.
a[x][y]='.';
//循环遍历移动的8个方向
for(int dx=-1;dx<=1;dx++){
for(int dy=-1;dy<=1;dy++){
//向x方向移动dx,向y方向移动dy,移动的结果位(nx,ny)
int nx=x+dx;
int ny=y+dy;
//判断(nx,ny)是不是在园子内,以及是否有积水
if(0<=nx&nx<n&&0<=ny&&ny<m&&a[nx][ny]=='W')
dfs(nx,ny);
}
}
}
}
宽度优先搜索
BFS
深度优先搜索运用了栈(先进后出)
宽度优先搜索则利用了队列
搜索时首先将初始状态添加到队列里面,然后从队列的最前段不断去除状态,把这种状态可以转移到的状态中尚未访问过的部分加入队列,如此往复,知道队列被取空或找到了问题的解。通过观察这个队列,我们可以就知道所有的状态都是按照初始状态由近及远的顺序被遍历的。
迷宫的最短路径
宽度优先搜索按照距开始状态由近及远的顺序进行搜索,因此可以很容易地用来求最短路径、最少操作之类问题的答案。
在这个问题中,状态仅仅是目前所在位置的坐标,因此可以构成pair或者编码成int来表示状态。当状态更加复杂时,就需要封装成一个类来表示状态了。转移的方式位四方向移动,状态数与迷宫的大小是相等的,所以复杂度是O(4NM)=O(N*M)
宽度优先搜索中,只需将已访问过的状态用标记管理起来,就可以很好地做到由近及远的搜索。
这个问题中由于需要求最短距离,不防用d[N][m]数组把最短距离保存起来。初始时用充分大的常数INS来初始化他,这样尚未到达的位置就是INF,也就同时取到了标记的作用
虽然到达终点时就会停止搜索,可如果继续下去直到队列位空的话,就可以计算出到各个位置的最短距离。此外,如果搜索到最后,d依然位inf的话,便可得知这个位置就是无法从起点到达的位置
在今后的程序中,使用像INF这样充分大的常数的情况还很多。不把INF当做例外,而是直接参与普通运算的情况也很常见。这种情况下,如果INF过大就可能带来溢出的危险。
代码写不出来
package 程序设计竞赛;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class 迷宫问题 {
private static int n;
private static int m;
private static char[][] a;
private static int INF=10000;;
private static int dx[]=new int[]{1,0,-1,0};;
private static int dy[]= new int[]{0,1,0,-1};
private static int[][] d=new int[n][m];;
private static int sx=0,sy=1;//起点坐标
private static int gx=9,gy=9;//终点坐标
//Pair<Integer,Integer> P=new Pair<Integer,Integer>();
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
a = new char[n][m];
for(int i=0;i<n;i++){
String s=sc.next();
for(int j=0;j<s.length();j++){
a[i][j]=s.charAt(j);
}
}
bfs();
}
//求从(sx,sy)到(gx,gy)的最短距离
//如果无法到达就是INF
public static void bfs(){
Queue<Point> que=new LinkedList<Point>();
//把所有的位置都初始化位INF
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
d[i][j]=INF;
}
}
//将起点加入队列,并把这一地点的距离设置位0
que.offer(new Point(sx,sy));
d[sx][sy]=0;
//不断循环知道队列的长度位0
while(!que.isEmpty()){
//从队列的最前段取出元素
Point p=que.poll();
//如果取出的状态已经是终点,则结束搜索
if(p.getX()==gx&&p.getY()==gy)
break;
//四个方向的循环
for(int i=0;i<4;i++){
//移动之后的位置记为(nx,ny)
int nx=p.getX()+dx[i];
int ny=p.getY()+dy[i];
//越界判断,障碍判断,是否走过判断
//判断是否可以移动以及是否以及访问过d[nx][ny]!=INF即为已经访问过
if(0<=nx&&nx<n&&0<=ny&&ny<m&&a[nx][ny]!='#'&&d[nx][ny]==INF){
//可以移动的话,则加入到队列,并且到该未知的额距离确定位到p的距离加1
que.offer(new Point(nx,ny));
d[nx][ny]=d[p.getX()][p.getY()]+1;
}
}
}
System.out.println(d[gx][gy]);
}
}
//点类,用于存储坐标(x,y)
class Point{
int x;
int y;
public int getX(){
return x;
}
public void setX(int x){
this.x=x;
}
public int getY(){
return y;
}
public void setY(int y){
this.y=y;
}
public Point(int x,int y){
super();
this.x=x;
this.y=y;
}
}
DFS适用于要遍历所有状态
在求最短路径的时候深度优先遍历比较好
书上写的特殊状态的枚举
就是全排列
c中可以使用next_permutation,把n个元素共n!中不同的排列出来
用位运算,可以枚举从n个元素中取出k个的共cnk中状态或是某个集合中的全部子集
剪枝