一些技巧1

237 阅读4分钟

一、打表法

当一个题目输入时是int, 输出时是int型或boolean型,4成以上可以采用打表法

先用傻代码得到规律,根据规律写出优化代码

题目一

小虎去附近的商店买苹果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个 每袋的包装包装不可拆分。可是小虎现在只想购买恰好n个苹果,小虎想购买尽 量少的袋数方便携带。如果不能购买恰好n个苹果,小虎将不会购买。输入一个 整数n,表示小虎想购买的个苹果,返回最小使用多少袋子。如果无论如何都不 能正好装下,返回-1。 image.png

普通代码

image.png

打表法代码

image.png

题目二

牛牛和羊羊都很喜欢青草。今天他们决定玩青草游戏。 最初有一个装有n份青草的箱子,牛牛和羊羊依次进行,牛牛先开始。在每个回合中,每个 玩家必须吃一些箱子中的青草,所吃的青草份数必须是4的x次幂,比如1,4,16,64等等。 不能在箱子中吃到有效份数青草的玩家落败。假定牛牛和羊羊都是按照最佳方法进行游 戏,请输出胜利者的名字。

普通代码
    public static String winner1(int n){
        //依次让先手牛先吃1分,4分。。。如果出现可以赢的情况,就是赢家是先手牛
        int base = 1;//先手牛先吃的草
        while (base <= n){
            //当前一共有n份草,先手吃掉的是base份,n - base 是留给后手的草
            //如果n - base是后手赢了,也就是算上base份草的时候是先手赢了
            if (winner1(n - base).equals("后手")){
                return "先手";
            }
            if (base > n / 4){
                break;//为了防止溢出
                //注意不可以写成(base * 4 > n)因为你在算(base * 4)的时候就已经溢出了
            }
            base *= 4;
        }
        return "后手";//所有的都尝试过了,没有返回先手赢就只能后手赢了
    }

    public static void main(String[] args) {
        System.out.println(winner1(0));
    }
打表法代码

image.png

    public static String winner2(int n){
        if (n % 5 == 0 || n % 5 == 2){
            return "后手";
        }else {
            return "先手";
        }
    }

二、预处理法

题目一

牛牛有一些排成一行的正方形。每个正方形已经被染成红色或者绿色。牛牛现在可 以选择任意一个正方形然后用这两种颜色的任意一种进行染色,这个正方形的颜色将 会被覆盖。牛牛的目标是在完成染色之后,每个红色R都比每个绿色G距离最左侧近。 牛牛想知道他最少需要涂染几个正方形。 如样例所示: s = RGRGR 我们涂染之后变成RRRGG满足要求了,涂染的个数为2,没有比这个更好的涂染方案。

预处理法代码

统计 i ~ N - 1 上的R的数量,以便知道后面直接涂成G的时候的个数 统计 0 ~ i 上的G的数量,以便知道前面直接涂成R的时候的个数 以便可以及时的取出,不需要重复的计算

	// RGRGR -> RRRGG
	public static int minPaint(String s) {
		if (s == null || s.length() < 2) {
			return 0;
		}
		char[] chs = s.toCharArray();
		int[] right = new int[chs.length];
		//统计 i ~ N - 1上的R的数量,以便知道后面直接涂成G的时候的个数
		right[chs.length - 1] = chs[chs.length - 1] == 'R' ? 1 : 0;
		for (int i = chs.length - 2; i >= 0; i--) {
			right[i] = right[i + 1] + (chs[i] == 'R' ? 1 : 0);
		}
		//假设min的最大值是number[0],也就是全变成G的数量
		int res = right[0];
		int left = 0;
		for (int i = 0; i < chs.length - 1; i++) {
			left += chs[i] == 'G' ? 1 : 0;
			//left 存的是0 ~ i上G的数量
			res = Math.min(res, left + right[i + 1]);
		}
		res = Math.min(res, left + (chs[chs.length - 1] == 'G' ? 1 : 0));
		return res;
	}

	public static void main(String[] args) {
		String test = "GGGGGR";
		System.out.println(minPaint(test));

	}

题目二

给定一个N * N的矩阵matrix,只有0和1两种值,返回边框全是1的最大正方形的边 长长度。 例如:

01111

01001

01001

01111

01011 其中边框全是1的最大正方形的大小为4 * 4,所以返回4。

普通法

image.png

预处理方法代码

可以优化最后一步是不是上面的值都是1,建立两个矩阵,一个存当前点右方有多少个连续的1,一个存当前点下方有多少个连续的1。

这个和简单代码的思路是不同的,这个更加的简洁,先从最长的长度开始找,看有没有符合条件的。

	public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
		int r = m.length;
		int c = m[0].length;
		//前面的处理都是为了下面循环的时候,不会越界
		if (m[r - 1][c - 1] == 1) {
			right[r - 1][c - 1] = 1;
			down[r - 1][c - 1] = 1;
		}
		for (int i = r - 2; i != -1; i--) {
			if (m[i][c - 1] == 1) {
				right[i][c - 1] = 1;
				down[i][c - 1] = down[i + 1][c - 1] + 1;
			}
		}
		for (int i = c - 2; i != -1; i--) {
			if (m[r - 1][i] == 1) {
				right[r - 1][i] = right[r - 1][i + 1] + 1;
				down[r - 1][i] = 1;
			}
		}
		for (int i = r - 2; i != -1; i--) {
			for (int j = c - 2; j != -1; j--) {
				if (m[i][j] == 1) {
					right[i][j] = right[i][j + 1] + 1;
					down[i][j] = down[i + 1][j] + 1;
				}
			}
		}
	}

	public static int getMaxSize(int[][] m) {
		int[][] right = new int[m.length][m[0].length];
		int[][] down = new int[m.length][m[0].length];
		setBorderMap(m, right, down);
		//先从最长的长度开始找,看有没有符合条件的
		//这个和简单代码的思路是不同的,这个更加的简洁
		for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
			if (hasSizeOfBorder(size, right, down)) {
				return size;
			}
		}
		return 0;
	}

	public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
		for (int i = 0; i != right.length - size + 1; i++) {
			for (int j = 0; j != right[0].length - size + 1; j++) {
				if (right[i][j] >= size && down[i][j] >= size
						&& right[i + size - 1][j] >= size
						&& down[i][j + size - 1] >= size) {
					return true;
				}
			}
		}
		return false;
	}

	public static int[][] generateRandom01Matrix(int rowSize, int colSize) {
		int[][] res = new int[rowSize][colSize];
		for (int i = 0; i != rowSize; i++) {
			for (int j = 0; j != colSize; j++) {
				res[i][j] = (int) (Math.random() * 2);
			}
		}
		return res;
	}

	public static void printMatrix(int[][] matrix) {
		for (int i = 0; i != matrix.length; i++) {
			for (int j = 0; j != matrix[0].length; j++) {
				System.out.print(matrix[i][j] + " ");
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		int[][] matrix = generateRandom01Matrix(7, 8);
		printMatrix(matrix);
		System.out.println(getMaxSize(matrix));
	}

题目三

给定一个函数f,可以1~5的数字等概率返回一个。请加工出1~7的数字等概率 返回一个的函数g。

预处理法代码
    public static int f(){
        return (int) (Math.random() * 5) + 1;
    }

    // 等概率返回0和1的函数
    public static int r01(){
        int res = 0;
        do {
            res = f();
            //如果扔出3就重新仍
        }while (res == 3);
        return res < 3 ? 0 : 1;
    }

    public static int g(){ //等概率1 ~ 7
        int res = 0;
        do {
            res = (r01() << 2) + (r01() << 1) + r01();
            //保证等概率0 ~ 6 仍出7就重新仍
        }while (res == 7);
        return res + 1;
    }

题目四

给定一个非负整数n,代表二叉树的节点个数。返回能形成多少种不同的二叉树结构

预处理法代码
    public static int process(int n){
        if (n < 0){
            return 0;
        }
        if (n == 0){
            return 1;
        }
        if (n == 2){
            return 2;
        }
        int res = 0;
        for (int leftNum = 0; leftNum <= n - 1; leftNum++){
            int leftWays = process(leftNum);
            int rightWays = process(n - 1 - leftNum);
            res += leftWays * rightWays;
        }
        return res;
    }