三省吾身 1.0
1. 递归
① 递归实现指数型枚举
🤠 原题地址
import java.util.*;
class Main{
static int N = 16,n;
static int[] st = new int[N];// 0 表示默认, 1 表示选 ,2 表示不选
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(0);
}
static void dfs(int u)
{
if(u >= n){//递归出口
for(int i = 0; i < n; i++)
if(st[i] == 1)
System.out.print(i+1+" ");
System.out.println();
return ;
}
st[u] = 1; // 选
dfs(u+1);
st[u] = 0;// 恢复现场
st[u] = 2;// 不选
dfs(u+1);
st[u] = 0;//恢复现场
}
}
② 递归实现排列型枚举
🤠 时间复杂度:O( n*n! )
🤠 大量输出时,使用 BufferedWriter ,注意抛异常
🤠 原题地址
import java.util.*;
public class Main
{
static int N = 10, n;
static int[] st = new int[N];// 表示每个坑位的状态
static boolean[] used = new boolean[N];// 标记某个数是否被使用过
public static void main(String[] args)
{
n = new Scanner(System.in).nextInt();
dfs(1);// dfs每个坑位
}
private static void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i++)
{
System.out.print(st[i] + " ");
}
System.out.println();
return;
}
// 枚举每一种选择
for (int i = 1; i <= n; i++)
{
if (!used[i])
{
used[i] = true;
st[u] = i;
dfs(u + 1);
// 恢复现场
used[i] = false;
st[u] = 0;
}
}
}
}
④ 递归实现组合类型枚举
🤠 原题地址
import java.io.*;
import java.util.*;
class Main
{
static 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 sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
dfs(0, 1);
out.flush();
}
/**
* @param u 第几个坑位
* @param min 表示比前一个数大的最小数,实现组合数去重的效果
* @throws IOException
*/
static void dfs(int u, int min) throws IOException
{
// 减枝:特判掉不可能实现的方案,提前返回
if (u + n - min + 1 < m)
return;
// 递归出口,坑位填满
if (u >= m)
{
for (int i = 0; i < m; i++)
out.write(a[i]+" ");
out.write("\n");
return;
}
for (int i = min; i <= n; i++)
{
a[u] = i;// i 填入 第u个 坑位
dfs(u + 1, i + 1);
}
}
}
⑤ 带分数
🤠 参考地址
🤠 暴力枚举,枚举全排列,枚举每个数,怕是过不了
import java.util.Scanner;
public class 暴力带分数
{
static int N = 10;
static int target; // 题目给出的目标数
static int num[] = new int[N]; // 保存全排列的结果
static boolean[] used = new boolean[N];// 生成全排列过程中标记是否使用过
static int cnt;// 计数,最后输出的结果
// 计算某一段区间的数
static int calc(int l, int r)
{
int res = 0;
for (int i = l; i <= r; i++)
{
res = res * 10 + num[i];
}
return res;
}
// 全排列进行分段
static void dfs(int u)
{
if (u == 9)
{
// 一个序列分三个非空子序列,枚举 i j 两块隔板
for (int i = 0; i < 7; i++)
{
// 第一段最多就是7个数 其他两段一人一个
for (int j = i + 1; j < 8; j++)
{
// 分成三个区间
int a = calc(0, i);
int b = calc(i + 1, j);
int c = calc(j + 1, 8);
// 判定的话 是对n=a+b/c进行变换得到cn=ac+b
if (a * c + b == c * target)
cnt++;
}
}
}
// 全排列模板
for (int i = 1; i <= 9; i++)
{
if (!used[i])
{
used[i] = true;
num[u] = i;
dfs(u + 1);
used[i] = false;// 恢复现场
}
}
}
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
target = sc.nextInt();
dfs(0);
System.out.println(cnt);
}
}
🤠 公式化简,少枚举一个变量
import java.util.*;
public class 带分数
{
static int N = 10;
static int n, ans;
static boolean[] st = new boolean[N];// 记录某个数是否已经被使用
public static void main(String[] args)
{
n = new Scanner(System.in).nextInt();
dfs_a(0, 0);
System.out.println(ans);
}
/**
* @param u 每个坑位
* @param a 当前 a 的值
*/
static void dfs_a(int u, int a)
{
if (a >= n || u > 9)
return;
if (a != 0)
{
// 先枚举 a ,再根据 a 来枚举 c
dfs_c(u, a, 0);
}
for (int i = 1; i < 10; i++)
{
if (!st[i])
{
st[i] = true;
dfs_a(u + 1, a * 10 + i);
st[i] = false;
}
}
}
/**
* @param u 记录坑位
* @param a a 的值
* @param c 枚举 c 然后 求b判断是否合法
*/
static void dfs_c(int u, int a, int c)
{
// 坑位用完 非法
if (u == n)
{
return;
}
if (check(a, c))
ans++;
// 根据已定的 a 和坑位 ,枚举 c 的所有可能
for (int i = 1; i <= 9; i++)
{
if (!st[i])
{
st[i] = true;
dfs_c(u + 1, a, c * 10 + i);
st[i] = false;// 恢复现场
}
}
}
static boolean check(int a, int c)
{
// n = a + b/c -> nc = ac + b 已知 n 枚举 a c 推出 b ,看 b 符不符合条件
int b = n * c - a * c;
// 剪枝
if (a <= 0 || b <= 0 || c <= 0)
return false;
// 需要在之前的使用状态上进行判重,但也要修改状态,所以得拿备份数组操作
boolean[] backup = Arrays.copyOf(st, st.length);
// 枚举 b 的每一位是否符合题目要求
while (b > 0)
{
int x = b % 10;// 取出个位
b /= 10;// 删除各位
if (x == 0 || backup[x] == true)
{
return false;
}
backup[x] = true;
}
// 枚举 1~9 看是否用上了
for (int i = 1; i < 10; i++)
{
if (!backup[i])
return false;
}
return true;
}
}
2. 递推
① 费解的开关
🤠 原题地址
🤠 Arrays.copyOf(原数组,长度) :不能直接拷贝二维数组
import java.io.*;
import java.util.Arrays;
public class Main
{
static int N = 6;
static int[][] a = new int[N][N];
static int[][] copy = new int[N][N];
static int[] dx = { 0, 0, 1, 0, -1 };
static int[] dy = { 0, -1, 0, 1, 0 };
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws Exception
{
int t = Integer.parseInt(in.readLine());
while (t-- > 0)
{
for (int i = 1; i <= 5; i++)
{
String[] split = in.readLine().split("");
for (int j = 1; j <= 5; j++)
a[i][j] = Integer.parseInt(split[j - 1]);
}
int res = 100;
// 枚举第一行的所有方案 2^5 = 32,用二进制上的每一位表示每一格子上的操作,1 表示 按,0表示不按
for (int op = 0; op < 32; op++)
{
// 因为要操作原数组数据,所以得建造副本
// 二维数组不能直接全拷贝,直接拷贝是共用一份数据
// copy = Arrays.copyOf(a, a.length * 6);
for (int i = 1; i < a.length; i++)
{
copy[i] = Arrays.copyOf(a[i], a[i].length);
}
/* 循环深拷贝
for (int i = 1; i <= 5; i++)
for (int j = 1; j <= 5; j++)
copy[i][j] = a[i][j];
*/
// System.out.println(a);
// System.out.println(copy);
int step = 0; // 记录本方案的操作步数
// 进行第一行的操作
for (int i = 0; i < 5; i++)
{
int x = op >> i & 1;// 方案 op 第 i位上的抉择:按与不按
if (x == 1)
{
step++;
turn(1, i + 1);
}
}
// 进行后三行的操作
for (int i = 2; i <= 5; i++)// 枚举行号
{
for (int j = 1; j <= 5; j++)// 枚举当前行的上一行的每一列
{
if (copy[i - 1][j] == 0)
{
turn(i, j);
step++;
}
}
}
boolean flag = true;
// 最后,检查最后一行是否全部点亮
for (int i = 1; i <= 5; i++)
{
if (copy[5][i] == 0)
flag = false;
}
if (flag)
{
res = Math.min(res, step);
}
}
if (res >= 7)
System.out.println(-1);
else
{
System.out.println(res);
}
in.readLine();
}
}
/**
* @param r 行号
* @param c 列号
*/
private static void turn(int r, int c)
{
for (int i = 0; i < 5; i++)
{
int x = r + dx[i];
int y = c + dy[i];
if (x > 0 && x <= 5 && y > 0 && y <= 5)
{
copy[x][y] ^= 1;// 是 1 就变成 0 , 0 就变成 1
}
}
}
}

浙公网安备 33010602011771号