三省吾身 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
			}
		}
	}
}
posted @ 2023-02-28 16:33  兑生  阅读(21)  评论(0)    收藏  举报  来源
Live2D