初级DFS练习:POJ1321 POJ2488 POJ3009
POJ1321
详细代码:
package poj1321;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/10/4 14:13
* @Description 题目大意:给定一个不规则棋盘和k个棋子,
* 要求把k个棋子全放在棋盘上,并且不能在同一行或同一列,
* 问有几种放法?
* 解题思路:DFS每层节点对应一列,遍历每列所有棋盘空位,
* 选一个空位放一个棋子或者本列不放棋子。
* @Since version-1.0
*/
public class Main {
/**
* 棋盘范围
*/
static int n;
/**
* 棋子数
*/
static int k;
/**
* 棋盘
*/
static char[][] board;
/**
* 标记被用过的列
*/
static int[] check;
/**
* 方案数
*/
static long schemes;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
n = sc.nextInt();
k = sc.nextInt();
if (n == -1 && k == -1) {
break;
}
board = new char[n + 5][n + 5];
check = new int[n + 5];
for (int i = 0; i < n; i++) {
String str = sc.next();
for (int j = 0; j < n; j++) {
board[j][i] = str.charAt(j);
}
}
schemes = 0;
dfs(0, 0);
System.out.println(schemes);
}
}
/**
* @param c 从第几列开始
* @param ck 当前已放置棋子数
*/
private static void dfs(int c, int ck) {
/*放够棋子,方案+1*/
if (ck == k) {
schemes++;
return;
}
/*出界剪枝*/
if (c == n) {
/*出界*/
return;
}
/*遍历一列*/
for (int i = 0; i < n; i++) {
/*如果有空的棋盘格就放一个棋*/
if (board[c][i] == '#' && check[i] == 0) {
check[i] = 1;
dfs(c + 1, ck + 1);
check[i] = 0;
}
}
/*当前列也可以选择不放棋*/
dfs(c + 1, ck);
}
}
/*
题目示例的数据太完美了,很难测出错误,这里改点复杂的测试数据。
输入:
2 1
#.
.#
4 2
...#
..#.
.#..
#...
8 3
..#.#.#.
...##.#.
.#.#..#.
#.#....#
.##....#
....#.#.
..#.#...
.#...#..
-1 -1
输出:
2
6
665
*/
POJ2488
[题目链接]( 2488 -- A Knight's Journey (poj.org) )
详细代码:
package poj2488;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/9/26 14:43
* @Description 题目大意:骑士生活在一个p*q格的棋盘上,他每次行走都是
* 一个方向上2格加上垂直于此的方向上1格,一个日字形对角的走法
* 棋盘的横坐标是1-p,纵坐标是字母表序的q个大写字母A,B...,
* 左上角第一格坐标是A1,骑士想要走遍每一个格,而且要求路径的
* 坐标代号是按字典序从小到大的。
* 解题思路:字典序最小的是A1,所以把起点设为A1,每次搜索的方向有8个,
* 画个图就能发现,不管你在哪个点,对应8个方向的落脚点坐标的字典序
* 是固定的,所以只要通过方向数组设定搜索8个方向的顺序,
* 最后得到的路径一定是字典序的。
* @Since version-1.0
*/
public class Main {
/**
* 案例数
*/
static int n;
/**
* 长
*/
static int p;
/**
* 宽
*/
static int q;
/**
* 棋盘
*/
static int[][] board;
/**
* 字典序方向
*/
static int[][] dir = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
/**
* 当前路径
*/
static String[] currPath;
/**
* 最终路径
*/
static String[] finalPath;
/**
* 格子数
*/
static int cells;
/**
* 搜索开关
*/
static boolean search;
/**
* 可能性
*/
static boolean possibility;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
// long st = System.currentTimeMillis();
/*读取数据*/
for (int i = 0; i < n; i++) {
q = sc.nextInt();
p = sc.nextInt();
board = new int[p][q];
board[0][0]=1;
cells = p*q;
currPath = new String[cells];
finalPath = new String[cells];
currPath[0] = "A1";
search = true;
possibility = false;
System.out.println("Scenario #"+(i+1)+":");
if (cells==1){
System.out.println("A1");
System.out.println(" ");
}else {
dfs(0,0,1);
if (possibility){
for (int j = 0; j < cells; j++) {
if (j==cells-1){
System.out.println(finalPath[j]);
}else {
System.out.print(finalPath[j]);
}
}
}else {
System.out.println("impossible");
}
System.out.println("");
}
}
/* long e = System.currentTimeMillis();
System.out.println("计算时长:"+(e-st)+"ms");*/
}
/**
* @param x 当前横坐标
* @param y 当前纵坐标
* @param c 当前走过的格子数
*/
private static void dfs(int x,int y,int c) {
/*遍历完所有格子*/
if (c==cells){
/*结束递归*/
search = false;
/*标记成功*/
possibility = true;
/*记录最优解*/
for (int i = 0; i < cells; i++) {
finalPath[i]=currPath[i];
}
return;
}
/*8个方向搜索*/
for (int i = 0; i < 8 && search; i++) {
int a = x+dir[i][0];
int b = y+dir[i][1];
/*下一步不出界且没走过*/
if (a>=0&&a<p&&b>=0&&b<q&&board[a][b]==0){
board[a][b] = 1;
/*生成题目要求格数的坐标*/
String str = (char)('A'+a)+""+(b+1);
currPath[c] = str;
dfs(a,b,c+1);
board[a][b] = 0;
currPath[c] = "";
}
}
}
}
/*
输入:
3
1 1
2 3
4 3
输出:
Scenario #1:
A1
Scenario #2:
impossible
Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4
*/
POJ3009
[题目链接]( 3009 -- Curling 2.0 (poj.org) )
详细代码:
package poj3009;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/9/25 17:07
* @Description 题目大意:冰壶游戏,地图数组中2是起点,3是终点,1是石头,
* 0是空地,冰壶初始在2,可以往上下左右四个方向投掷,投了以后会沿着
* 一个方向一直滑行,直到碰到石头会停止在石头前,同时石头会碎掉,
* 1变成0,冰壶停下来以后就可以再次选一个方向投掷,如果冰壶出界会
* 视为游戏失败,如果投掷10次还没到达终点3也失败,给一个地图求最少
* 投掷几次到达终点3,打印次数,如果无法到达则打印-1。
* 解题思路:外循环遍历4个方向,内循环就往当前方向一直移动,
* 直到碰到石头则确定当前位置,更改地图石块,递归再遍历4个方向,直到找到终点。
* @Since version-1.0
*/
public class Main {
/**
* 场地宽
*/
static int w;
/**
* 场地高
*/
static int h;
/**
* 游戏场地
*/
static int[][] gameBoard;
/**
* 起点x
*/
static int sx;
/**
* 起点y
*/
static int sy;
/**
* 终点x
*/
static int gx;
/**
* 终点y
*/
static int gy;
/**
* 当前投掷次数
*/
private static int currThrowsNum;
/**
* 最小投掷次数
*/
private static int minThrowsNum;
/**
* 方向数组
*/
private static final int[] DIR_X = {0,1,0,-1};
private static final int[] DIR_Y = {1,0,-1,0};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
w = sc.nextInt();
h = sc.nextInt();
if (w==0&&h==0){
break;
}
gameBoard = new int[w][h];
/*读取上面那行的末尾空格*/
String str = sc.nextLine();
for (int i = 0; i < h; i++) {
/*读取下一行数字*/
str = sc.nextLine();
/*按空格分割成数组*/
String[] s = str.split(" ");
for (int j = 0; j < s.length; j++) {
/*字符转整数*/
int temp = Integer.parseInt(s[j]);
gameBoard[j][i]= temp;
/*判断起点位置*/
if (temp==2){
sx = j;
sy = i;
}/*判断终点位置*/
if (temp==3){
gx = j;
gy = i;
}
}
}
currThrowsNum = 0;
minThrowsNum = -1;
dfs(sx,sy);
System.out.println(minThrowsNum);
}
}
/**
* @param x 当前位置x坐标
* @param y 当前位置y坐标
*/
private static void dfs(int x,int y) {
/*往四个方向搜索*/
for (int i = 0; i < 4; i++) {
/*当前冰壶移动到的位置*/
int cx = x;
int cy = y;
/*移动格数*/
int cellsNum = 0;
/*试探移动格数*/
for (int j = 0; j <= Math.max(w, h); j++) {
cx += DIR_X[i];
cy += DIR_Y[i];
cellsNum++;
/*如果当前方向会出界则直接跳出循环换个方向*/
if(cx<0||cx>=w||cy<0||cy>=h){
break;
}
/*如果当前方向和阻碍块接触直接跳出循环换个方向*/
if (gameBoard[cx][cy]==1&&cellsNum==1){
break;
}
/*如果到达终点,则更新最小移动次数*/
if (cx==gx&&cy==gy){
/*当前投掷次数+1*/
currThrowsNum++;
if (currThrowsNum<=10&&(minThrowsNum ==-1|| currThrowsNum < minThrowsNum)){
minThrowsNum = currThrowsNum;
}
currThrowsNum--;
/*到达终点就没必要再往其他方向走了,当前子树结束了*/
return;
}
/*如果撞到阻碍块且符合条件*/
if (gameBoard[cx][cy]==1&&cellsNum>1){
/*当前投掷次数+1*/
currThrowsNum++;
/*投掷次数=10,直接回溯到上一步换个方向*/
if (currThrowsNum==10){
currThrowsNum--;
break;
}
/*当前阻碍块消失a*/
gameBoard[cx][cy] = 0;
/*后退一步*/
cx -= DIR_X[i];
cy -= DIR_Y[i];
dfs(cx,cy);
/*回溯; */
currThrowsNum--;
cx += DIR_X[i];
cy += DIR_Y[i];
gameBoard[cx][cy] = 1;
/*跳出当前方向*/
break;
}
}
}
}
}
/*
输入:
2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0
输出:
1
4
-1
4
10
-1
*/