AcWing蓝桥杯辅导课:第一讲递推与递归

AcWing 92. 递归实现指数型枚举

在这里插入图片描述
思路:

方法一: 暴力枚举

用二进制加位运算枚举每一个状态,输出即可,时间复杂度为 O ( N 2 N ) O(N2^N) O(N2N)

代码:

import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/22 11:08
 */
public class Main {
    static Scanner scanner = new Scanner(System.in);
    static int n;

    public static void main(String[] args) {
        n = scanner.nextInt();
        for (int i = 0; i < (1 << n); i++) {
            for (int j = 0; j < n; j++) {
                if (((i >> j) & 1) == 1) {
                    System.out.print((j + 1) + " ");
                }
            }
            System.out.println();
        }
    }

}

方法二: 深搜

用一个数组存储每个数字的状态, 0表示不放, 1表示放,如果当前状态是1的话,就输出该数,与求全排列代码类似。

import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/22 11:08
 */
public class Main {
    static Scanner scanner = new Scanner(System.in);
    static final int N = 20;
    static int n;
    // 0不选 1选
    static int[] st = new int[N];

    public static void main(String[] args) {
        n = scanner.nextInt();
        dfs(1);
    }

    public static void dfs(int u) {
        if (u == n + 1) {
            for (int i = 1; i <= n; i++) {
                if (st[i] == 1) System.out.print(i + " ");
            }
            System.out.println();
            return;
        }

        st[u] = 0;
        dfs(u + 1);
        st[u] = 1;
        dfs(u + 1);
    }

}

AcWing 94. 递归实现排列型枚举

在这里插入图片描述
思路:
深度优先搜索,时间复杂度为 O ( N ∗ N ! ) O(N*N!) O(NN!),注意Java 的 System.out.println() 输出效率低, 本题会超时,所以用 BufferedWrite 来输出。

代码:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/22 10:25
 */
public class Main {
    static final int N = 15;
    static int n;
    static boolean[] st = new boolean[N];
    static int[] a = new int[N];
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dfs(0);
        out.flush();
    }

    public static void dfs(int step) throws IOException {
        if (step == n) {
            for (int i = 0; i < n; i++)
                out.write(a[i] + " ");
            out.write('\n');
        } else {
            for (int i = 1; i <= n; i++) {
                if (!st[i]) {
                    a[step] = i;
                    st[i] = true;
                    dfs(step + 1);
                    st[i] = false;
                }
            }
        }
    }
}

AcWing 717. 简单斐波那契

在这里插入图片描述
代码:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/22 10:43
 */
public class Main {
    static Scanner scanner = new Scanner(System.in);
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    static final int N = 50;
    static int[] fi = new int[N];
    static int n;

    public static void main(String[] args) throws IOException {
        n = scanner.nextInt();
        fi[1] = 0;
        fi[2] = 1;
        for (int i = 3; i <= n; i++) {
            fi[i] = fi[i - 1] + fi[i - 2];
        }
        for (int i = 1; i <= n; i++) {
            out.write(fi[i] + " ");
        }
        out.flush();
    }
}

AcWing 95. 费解的开关

在这里插入图片描述

思路:

考虑第一行,有 2 n 2 ^ n 2n 种不同的状态。对于第一行的每个灯的状态,由于每个开关状态的改变会影响上下左右的所有开关的状态,所以在第一行,如果某灯是灭的话,有且仅有该灯下面第二行的开关的改变能影响该灯的状态,也就是说,只有正下方的开关可以改变上一层的状态,第 n n n 行 确定 n + 1 n + 1 n+1 行的状态,第一行确定整个的状态,所以只需要用二进制枚举第一行的状态即可,判断最后一行是否都为亮的,如果都是亮的,则有可行解,再判断可行解与 6 的 关系。

为保证不同的操作方式之间的结果不干扰,一开始要对原始数组先备份,然后再还原。

代码:

import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/23 15:46
 */
public class Main {

    static final int N = 6;
    static char[][] g = new char[N][N], backup = new char[N][N];
    static int n;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();

        while (n-- != 0) {

            for (int i = 0; i < 5; i++) {
                String s = scanner.next();
                g[i] = s.toCharArray();
            }

            int res = 10;


            for (int op = 0; op < (1 << 5); op++) {

                for (int j = 0; j < 5; j++) {
                    backup[j] = Arrays.copyOf(g[j], 5);
                }

                int step = 0;

                for (int i = 0; i < 5; i++) {
                    if ((op >> i & 1) == 1) {
                        step++;
                        turn(0, i);
                    }
                }

                for (int i = 0; i < 4; i++) {
                    for (int j = 0; j < 5; j++) {
                        if (g[i][j] == '0') {
                            step++;
                            turn(i + 1, j);
                        }
                    }
                }
                boolean flag = false;

                for (int i = 0; i < 5; i++) {
                    if (g[4][i] == '0') {
                        flag = true;
                        break;
                    }
                }

                if (!flag) res = Math.min(res, step);
                for (int j = 0; j < 5; j++) {
                    g[j] = Arrays.copyOf(backup[j], 5);
                }

            }

            if (res > 6) System.out.println(-1);
            else System.out.println(res);
        }

    }


    public static void turn(int x, int y) {
        int[] dx = {-1, 1, 0, 0, 0}, dy = {0, 0, -1, 1, 0};

        for (int i = 0; i < 5; i++) {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= 5 || b < 0 || b >= 5) continue;
            g[a][b] ^= 1;
        }
    }
}

AcWing 93. 递归实现组合型枚举

在这里插入图片描述
思路:

搜索一个数,然后搜索下一个比该数大的数。

剪枝条件: 设当前搜索的是 第 u u u 个数,则已经有了 u − 1 u - 1 u1 个数,搜索到了 s t a r t start start ,一共有 n n n 个数,还剩 n − s t a r t + 1 n - start + 1 nstart+1 个数。

如果 u − 1 + n − s t a r t + 1 < m u - 1 + n - start + 1 \lt m u1+nstart+1<m,凑不齐 m m m 个数的组合,就剪去该分支。

代码:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/22 14:38
 */
public class Main {
    static final int N = 30;
    static int n, m;
    static int[] a = new int[N];
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();

        dfs(1, 1);
        out.flush();
    }

    public static void dfs(int step, int start) throws IOException {
        if (step + n - start < m) return;
        if (step == m + 1) {
            for (int i = 1; i <= m; i++)
                out.write(a[i] + " ");
            out.write('\n');
        } else {
            for (int i = start; i <= n; i++) {
                a[step] = i;
                dfs(step + 1, i + 1);
            }
        }
    }
}

AcWing 1209. 带分数

在这里插入图片描述
思路:

方法一: 暴力枚举全排列,枚举 a a a, b b b , c c c。枚举 a a a, b b b , c c c过程中配以剪枝, a a a的位数不超过6位。

代码:

import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/23 10:08
 */
public class Main {
    static final int N = 15;
    static int ans;
    static int n;
    static int[] arr = new int[N];
    static boolean[] st = new boolean[N];
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dfs(1);
        System.out.println(ans);
    }

    public static int cal(int a, int b) {
        int sum = 0;

        for (int i = a; i <= b; i++) sum = sum * 10 + arr[i];

        return sum;
    }

    public static void dfs(int u) {
        if (u > 9) {
        // 枚举a, b, c
            for (int i = 1; i <= 6; i++) {
                for (int j = i + 1; j <= 8; j++) {
                    int a = cal(1, i);
                    int b = cal(i + 1, j);
                    int c = cal(j + 1, 9);
                    if (a * c + b == n * c) ans++;
                }
            }

            return;
        }
        for (int i = 1; i <= 9; i++) {
            if (!st[i]) {
                arr[u] = i;
                st[i] = true;
                dfs(u + 1);
                st[i] = false;
            }
        }
    }
}

方法二: 枚举 a a a, c c c。计算 b b b ,判断该组合是否不重不漏用完 1 ∼ 9 1\sim 9 19 中的所有数。

代码:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/23 14:27
 */
public class Main {

    static final int N = 15;
    static boolean[] st = new boolean[N], backup = new boolean[N];
    static int n, ans;

    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        dfs_a(0, 0);
        System.out.println(ans);
    }

    public static void dfs_a(int u, int a) {
    	// 如果 a >= n 不满足条件 剪枝
        if (a >= n) return;
        // 在 a的基础上枚举c
        if (a != 0) dfs_c(u, a, 0);

        for (int i = 1; i <= 9; i++) {
            if (!st[i]) {
                st[i] = true;
                dfs_a(u + 1, a * 10 + i);
                st[i] = false;
            }
        }
    }

    public static void dfs_c(int u, int a, int c) {
        if (u > 9) return;
        if (check(a, c)) ans++;
        for (int i = 1; i <= 9; i++) {
            if (!st[i]) {
                st[i] = true;
                dfs_c(u + 1, a, c * 10 + i);
                st[i] = false;
            }
        }
    }

    public static boolean check(int a, int c) {
    	// 防止爆 int
        long b = n * (long) c - (long)a * c;
        if (a * b * c == 0) return false;
        for (int i = 1; i <= 9; i++) {
            backup[i] = st[i];
        }

        while (b != 0) {
            long x = b % 10;
            b /= 10;
            // 出现了0 或者b的数字与 a c 的有重复 不满足条件
            if (x == 0 || backup[(int)x]) return false;
            backup[(int)x] = true;
        }

		// 保证每个数字都用过了
        for (int i = 1; i <= 9; i++) {
            if (!backup[i]) return false;
        }

        return true;
    }
}

AcWing 116. 飞行员兄弟

在这里插入图片描述
思路:

因为本题规模不大,所以可以通过枚举和位运算来求解,一共有 16 个位置,则有 2 16 = 65536 2^{16} = 65536 216=65536 种状态,最后判断开关的状态。用ArrayList 来存储操作,仅当操作数更少的时候,才更新操作集。

代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/23 16:48
 */
public class Main {
    static final int N = 5;
    static char[][] g = new char[N][N], backup = new char[N][N];

    static class Node {
        int x, y;

        Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArrayList<Node> ans = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            String s = scanner.next();
            g[i] = s.toCharArray();
        }

        for (int op = 0; op < (1 << 16); op++) {

            for (int j = 0; j < 4; j++) {
                backup[j] = Arrays.copyOf(g[j], g[j].length);
            }

            ArrayList<Node> tmp = new ArrayList<>();


            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (((op >> (i * 4 + j)) & 1) == 1) {
                        turn(i, j);
                        tmp.add(new Node(i, j));
                    }
                }
            }

            boolean flag = false;
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (g[i][j] == '+') {
                        flag = true;
                        break;
                    }
                }
            }

            if (!flag) {
                if (ans.isEmpty() || ans.size() > tmp.size()) ans = tmp;
            }

            for (int j = 0; j < 4; j++) {
                g[j] = Arrays.copyOf(backup[j], backup[j].length);
            }

        }

        System.out.println(ans.size());
        for (Node tmp : ans) {
            System.out.println((tmp.x + 1) + " " + (tmp.y + 1));
        }

    }

    public static void turn(int x, int y) {
        for (int i = 0; i < 4; i++) {
            g[x][i] = g[x][i] == '+' ? '-' : '+';
            g[i][y] = g[i][y] == '+' ? '-' : '+';
        }
        g[x][y] = g[x][y] == '+' ? '-' : '+';

    }
}

AcWing 1208. 翻硬币

在这里插入图片描述
思路:

本题有不超过100个元素,枚举状态会超时,可以考虑贪心来做,如果两个字符串某个相同位置的元素不相同,就翻转,操作的次数就加一。这样只需要用到 O ( N ) O(N) O(N) 的时间复杂度。

代码:

import java.util.Scanner;

/**
 * @Description
 * @Author: PrinceHan
 * @CreateTime: 2023/2/24 9:54
 */
public class Main {
    static final int N = 105;
    static char[] s1 = new char[N], s2 = new char[N];
    static String start, end;
    static int n, ans;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        start = scanner.next();
        end = scanner.next();
        n = start.length();
        s1 = start.toCharArray();
        s2 = end.toCharArray();

        for (int i = 0; i < n - 1; i++) {
            if (s1[i] != s2[i]) {
                ans++;
                turn(i);
            }
        }

        System.out.println(ans);

    }

    public static void turn(int u) {
        s1[u] = s1[u] == '*' ? 'o' : '*';
        s1[u + 1] = s1[u + 1] == '*' ? 'o' : '*';
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值