初级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
*/

posted @ 2021-11-24 13:08  矜君  阅读(80)  评论(0编辑  收藏  举报