DFS练习: POJ2362 POJ2676 POJ2698 POJ3083 POJ3411

POJ2362

package poj2362;


import java.util.Scanner;

/**
 * @Author jinjun99
 * @Date Created in 2022/10/5 13:22
 * @Description
 * 用到了定序剪枝,遍历正方形的一条边时,是从n根小棒中选几根,前面跳过的后面不会再挑,
 * 将本次选择的小棒编号i加上1传给下一递归,下次递归接着从i+1开始遍历小棒数组。
 * 完成当前边再把下标调回0。
 * @Since version-1.0
 */
public class Main {
    static int side;
    static int n;
    static int[] sticks;
    static int[] check;

    static int succeed;

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();

        for (int i = 0; i < num; i++) {
            n = sc.nextInt();
            sticks = new int[n + 5];
            check = new int[n + 5];
            for (int j = 0; j < n; j++) {
                sticks[j] = sc.nextInt();
            }
            int sum = 0;
            int max = 0;
            for (int j = 0; j < n; j++) {
                sum += sticks[j];
                if (sticks[j]>max){
                    max = sticks[j];
                }
            }
            side = sum / 4;
            if (sum % 4 == 0 && side >= max) {
                succeed = 0;
                dfs(0,0, 0,0);
                if (succeed == 1) {
                    System.out.println("yes");
                } else {
                    System.out.println("no");
                }
            } else {
                System.out.println("no");
            }
        }
    }

    /**
     * @param currLen   当前长度
     * @param finishNum 当前完成棍子数
     */
    private static void dfs(int index,int currLen, int finishNum,int layer) {

        if (succeed==0){
            for (int i = index; i < n; i++) {
                if (currLen + sticks[i] < side && check[i] == 0) {
                    currLen += sticks[i];
                    check[i] = 1;

                    dfs(i+1,currLen, finishNum,layer+1);
                    check[i] = 0;
                    currLen -= sticks[i];
                }
                if (currLen + sticks[i] == side && check[i] == 0) {
                    finishNum++;
                    check[i] = 1;
                    if (finishNum == 3) {
                        succeed = 1;
                        return;
                    }
                    dfs(0,0, finishNum,layer+1);
                    finishNum--;
                    check[i] = 0;
                }

            }
        }

    }

}

POJ2676

package poj2676;

import java.util.Scanner;

/**
 * @Author jinjun99
 * @Date Created in 2022/10/11 15:09
 * @Description
 * @Since version-1.0
 */
public class Main {
    static int n = 10;
    static int spaceNum;
    static int[][] map;
    static boolean[][] row;
    static boolean[][] column;
    static boolean[][] grid;
    /**
     * 每行剩余的空位数
     */
    static int[] rowSpace;
    /**
     * 找到一行的解标记为true
     */
    static boolean filled;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        sc.nextLine();
        for (int i = 0; i < m; i++) {
            initData();

            for (int j = 1; j < n; j++) {
                String str = sc.nextLine();
                for (int k = 1; k < n; k++) {
                    int num = str.charAt(k - 1) - '0';
                    map[j][k] = num;
                    if (num > 0) {
                        rowSpace[j]--;
                        spaceNum--;
                        row[j][num] = true;
                        column[k][num] = true;
                        int g = 3 * ((j - 1) / 3) + (k - 1) / 3 + 1;
                        grid[g][num] = true;
                    }

                }
            }
            dfs(spaceNum);
            for (int j = 1; j < n; j++) {
                for (int k = 1; k < n; k++) {
                    if (k == n - 1) {
                        System.out.println(map[j][k]);
                    } else {
                        System.out.print(map[j][k]);
                    }
                }
            }
        }

    }

    private static void dfs(int sn) {
        if (sn==0){
            filled = true;
            return;
        }
        int[] index = findIndex();
        int x = index[0];
        int y = index[1];
        int k = 3 * ((x - 1) / 3) + (y - 1) / 3 + 1;
        for (int i = 1; i < n; i++) {
            if (!row[x][i] && !column[y][i] && !grid[k][i]) {
                /*三个条件都满足则填入数字*/
                map[x][y] = i;
                row[x][i] = true;
                column[y][i] = true;
                grid[k][i] = true;
                rowSpace[x]--;
                dfs(sn-1);
                /*如果前面的递归填完9行找到一个解,就在这里截断递归和回溯*/
                if (filled){
                    return;
                }
                rowSpace[x]++;
                map[x][y] = 0;
                row[x][i] = false;
                column[y][i] = false;
                grid[k][i] = false;
            }
        }

    }

    private static int[] findIndex(){
        int temp = 0;
        int[] index = new int[4];
        for (int i = 1; i < n; i++) {
            if (rowSpace[i]>temp){
                temp = rowSpace[i];
                index[0] = i;
            }
        }
        for (int i = 1; i < n; i++) {
            if (map[index[0]][i]==0){
                index[1]=i;
            }
        }
        return index;
    }
    private static void initData() {
        map = new int[n + 5][n + 5];
        row = new boolean[n + 5][n + 5];
        column = new boolean[n + 5][n + 5];
        grid = new boolean[n + 5][n + 5];
        rowSpace = new int[n];
        for (int i = 1; i < n; i++) {
            rowSpace[i] = 9;
        }
        spaceNum = 81;
        filled = false;

    }
}

/*
(1)用DFS搜索每个空格子。
(2)用位运算记录格子状态。每行、每列、每个九宫格,分别用一个9位的二进制数保存哪些数字还可以填。
对于每个位置,把它在的行,列,九宫格对应的数取 & 运算就可以得到剩余哪些数可以填。并用lowbit(x)取出能填的数。
(3)优化搜索顺序剪枝。从最容易确定数字的行(或列)开始填数,也就是0最少的行(或列);
在后续每个状态下,也选择0最少的行(或列)填数。
(4)可行性剪枝。每格填的数只能是对应行、列和宫中没出现过的。

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

*/

POJ2698

package POJ2698;

import java.util.Scanner;

/**
 * @Author jinjun99
 * @Date Created in 2022/9/30 13:21
 * @Description
 * @Since version-1.0
 */
public class Main {

    /**
     * 案例数
     */
    static int n;
    /**
     * DVD机数
     */
    static int k;
    /**
     * 当前DVD机里的DVD编号
     */
    static int[] driver;
    /**
     * 一个序列的DVD数
     */
    static int l;
    /**
     * DVD序列
     */
    static int[] sequence;
    /**
     * 最小插入次数
     */
    static int minInsertions;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        for (int i = 0; i < n; i++) {

            k = sc.nextInt();
            l = sc.nextInt();
            driver = new int[k+5];
            sequence = new int[l+5];
            for (int j = 0; j < l; j++) {
                sequence[j] = sc.nextInt();
            }
            minInsertions = k;
            /*先放k张碟进去*/
            for (int j = 0; j < k; j++) {
                /*加个保险*/
                if (j<l){
                    driver[j] = sequence[j];
                }else {
                    minInsertions = l;
                }
            }

            if (k<l){
                optimalPermutation();
            }
            System.out.println(minInsertions);
        }

    }

    private static void optimalPermutation() {
        /*遍历剩下的DVD序列*/
        for (int i = k; i < l; i++) {
            /*要取出的DVD机编号*/
            int index = 0;
            /*当前机内的DVD编号与后面序列中同编号的DVD的间隔*/
            int interval = 0;
            /*当前DVD编号不在机内*/
            boolean outside = true;
            /*遍历DVD机*/
            for (int j = 0; j < k; j++) {
                int dvd = driver[j];
                int dist = 0;
                /*当前DVD在机内*/
                if (dvd==sequence[i]){
                    outside = false;
                    break;
                }
                /*当前机内DVD已不在剩下的序列中*/
                boolean end = false;
                /*计算当前机内DVD与剩下的序列中最近同编号DVD的距离*/
                for (int m = i; m < l; m++) {
                    if (sequence[m]==dvd){
                        break;
                    }else {
                        dist++;
                        if (m==l-1){
                            end = true;
                        }
                    }
                }
                if (dist>interval||end){
                    index = j;
                    interval = dist;
                }
            }
            if (outside){
                driver[index] = sequence[i];
                minInsertions++;
            }
        }
    }
}

POJ3083

package poj3083;

import java.util.LinkedList;
import java.util.Scanner;

/**
 * @Author jinjun99
 * @Date Created in 2022/10/4 11:21
 * @Description 靠左靠右的路径,因为只有一条路,限制方向,不限制长短,可以用dfs或循环。
 * 求最短路径,多阶段决策问题求最少决策应该用BFS,因为以前先学的DFS,而且很多地图类题目,下意识用DFS,超时了。
 * @Since version-1.0
 */
public class Main {
    static int n;
    static int w;
    static int h;
    static char[][] map;
    static int[] start;
    static int[] end;
    static int rightPath;
    static int liftPath;

    static int minPath;
    static int frontDir;
    /**
     * 左前右后顺序的方向数组
     */
    static int[][] leftDir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
    /**
     * 右前左后顺序的方向数组
     */
    static int[][] rightDir = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}};

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        for (int i = 0; i < n; i++) {
            w = sc.nextInt();
            h = sc.nextInt();
            map = new char[w + 5][h + 5];
            start = new int[5];
            end = new int[5];
            for (int j = 0; j < h; j++) {
                String str = sc.next();
                for (int k = 0; k < w; k++) {
                    /*读取地图*/
                    map[k][j] = str.charAt(k);
                    /*读取起点和终点坐标*/
                    if (map[k][j] == 'S') {
                        start[0] = k;
                        start[1] = j;
                    }
                    if (map[k][j] == 'E') {
                        end[0] = k;
                        end[1] = j;
                    }
                }
            }
            rightPath = 1;
            liftPath = 1;
            frontDir = getFrontDir();
            getLeftOrRightPath(0);
            getLeftOrRightPath(1);
            minPath = 1;
            bfs();
            System.out.println(liftPath + " " + rightPath + " " + minPath);

        }

    }

    private static void bfs() {
        Node root = new Node(start[0],start[1],1 );
        LinkedList<Node> list = new LinkedList<Node>();
        list.offer(root);
        while (!list.isEmpty()){
            Node parent = list.poll();
            int childX,childY;
            int childStep = parent.step+1;
            if (minPath==1||childStep<minPath){
                for (int i = 0; i < 4; i++) {
                    childX = parent.x+leftDir[i][0];
                    childY = parent.y+leftDir[i][1];
                    boolean bl1 = childX>=0&&childX<w&&childY>=0&&childY<h;
                    if (bl1){
                        if (map[childX][childY]=='E'){
                            minPath = childStep;
                            break;
                        }
                        if (map[childX][childY]=='.'){
                            map[childX][childY]='#';
                            list.offer(new Node(childX,childY,childStep));
                        }
                    }
                }
            }
        }
    }
    static class Node{
        int x;
        int y;
        int step;

        public Node(int x, int y, int step) {
            this.x = x;
            this.y = y;
            this.step = step;
        }
    }
    /**
     * 获取一直靠左/右墙行走的路径长度
     *
     * @param searchDir 0是靠左,1是靠右
     */
    private static void getLeftOrRightPath(int searchDir) {
        /*当前坐标*/
        int x = start[0];
        int y = start[1];
        /*获取与当前正前方相对应的左边方向编号,按照左前右后的方向搜索*/
        int nextDir = turnLeft(frontDir);
        /*开始靠左或右墙走迷宫*/
        while (true) {
            for (int i = 0; i < 4; i++) {
                int nextX, nextY;
                /*下一步坐标*/
                if (searchDir == 0) {
                    nextX = x + leftDir[nextDir][0];
                    nextY = y + leftDir[nextDir][1];
                } else {
                    nextX = x + rightDir[nextDir][0];
                    nextY = y + rightDir[nextDir][1];
                }

                // 防止越界
                boolean bl1 = nextX >= 0 && nextY >= 0 && nextX < w && nextY < h;
                if (bl1) {
                    /* 到达终点*/
                    if (map[nextX][nextY] == 'E') {
                        if (searchDir == 0) {
                            liftPath++;
                        } else {
                            rightPath++;
                        }
                        return;
                    }
                    /*下一步可行*/
                    if (map[nextX][nextY] == '.') {
                        x = nextX;
                        y = nextY;
                        /*如果当前方向可行,则下一步从当前方向相对应的左/右方开始搜索*/
                        nextDir = turnLeft(nextDir);
                        if (searchDir == 0) {
                            liftPath++;

                        } else {
                            rightPath++;
                        }
                        break;
                    }
                }
                /*右转一个方向,按左前右后遍历4个方向*/
                nextDir = turnRight(nextDir);
            }
        }
    }

    /**
     * 左转
     *
     * @param dir 当前方向
     * @return
     */
    private static int turnLeft(int dir) {
        dir -= 1;
        if (dir >= 0) {
            return dir;
        } else {
            return 3;
        }
    }

    /**
     * 右转
     *
     * @param dir 当前方向
     * @return
     */
    private static int turnRight(int dir) {
        dir += 1;
        if (dir <= 3) {
            return dir;
        } else {
            return 0;
        }
    }

    /**
     * @return 找到起点的正前方的方向号码
     */
    private static int getFrontDir() {
        int fDir = 0;
        int a, b;
        for (int j = 0; j < 4; j++) {
            a = start[0] + leftDir[j][0];
            b = start[1] + leftDir[j][1];
            boolean bl = a >= 0 && a < w && b >= 0 && b < h;
            if (bl && map[a][b] == '.') {
                fDir = j;
                break;
            }
        }
        return fDir;
    }
}
/*
2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
9 5
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########
*/

POJ3411

package poj3411;

import java.util.Scanner;

/**
 * @Author jinjun99
 * @Date Created in 2022/10/17 22:44
 * @Description
 * @Since version-1.0
 */
public class Main {
    static int n;
    static int m;
    static class Rode{
        int a;
        int b;
        int c;
        int p;
        int r;

    }
    static Rode[] rodes;
    static int[] visit;
    static int minCost = 2000;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        rodes = new Rode[m+5];
        visit = new int[m+5];
        for (int i = 0; i < m; i++) {
            Rode rode = new Rode();
            rode.a = sc.nextInt();
            rode.b = sc.nextInt();
            rode.c = sc.nextInt();
            rode.p = sc.nextInt();
            rode.r = sc.nextInt();
            rodes[i] = rode;
        }
        dfs(1,0);
        if (minCost==2000){
            System.out.println("impossible");
        }else {
            System.out.println(minCost);
        }

    }

    private static void dfs(int city, int cost) {

        if (city==n){
            if (cost<minCost){
                minCost = cost;
            }
            return;
        }
        for (int i = 0; i < m; i++) {
            int b = rodes[i].b;
            if (city == rodes[i].a&&visit[b]<=3){
                visit[b]++;
                if (visit[rodes[i].c]>0){
                    dfs(b,cost+rodes[i].p);
                }else {
                    dfs(b,cost+rodes[i].r);
                }
                visit[b]--;
            }
        }
    }

}
/*
4 5
1 2 1 10 10
2 3 1 30 50
3 4 3 80 80
2 1 2 10 10
1 3 2 10 50
*/
posted @ 2022-10-19 20:01  矜君  阅读(27)  评论(0编辑  收藏  举报