打表找规律——合集

本文探讨了编程中利用打表法解决优化问题的策略,包括苹果分装和青草分配游戏。对于苹果分装问题,提出了通过暴力计算和寻找规律来减少袋子使用的方法。而在青草分配游戏中,分析了先手和后手的获胜策略。此外,还讨论了如何判断一个数是否能表示为连续正数之和,以及涉及数组状态变换的博弈问题。文章强调了数学原理在编程中的重要性,并提倡通过暴力求解观察规律来简化问题。
摘要由CSDN通过智能技术生成
package CallTable

import "testing"

/*
打表法

1.问题如果返回值不太多,可以用hardcode的方式列出,作为程序的一部分
2.一个大问题解决时底层使用规模不大的小问题的解,如果小问题的返回值满足条件1,可以
把小问题的解列成一张表,作为程序的一部分
3.打表找规律,有关1和2 内容后序会解决
 */

/*
打表找规律
1.某个面试题,输入参数类型简单,并且只有一个实际参数
2.要求的返回值类型也简单,并且只有一个
3.用暴力方法,把输入参数对应的返回值,打印出来看看,进而优化code
 */


/*
题目一

小虎去买苹果,商店只提供两种类型的塑料袋,每种类型都有任意数量。
1. 能装下6个苹果的袋子
2. 能装下8个苹果的袋子
小虎可以自由使用两种袋子来装苹果,但是小虎有强迫症,他要求自己使用的袋子数量必须最少,且使用的每个袋子必须装满。
给定一个正整数N,返回至少使用多少个袋子。如果N无法让使用的每个袋子必须装满,返回-1



写暴力方法,找规律,打表
N = 100
8号袋子  100 /8  12  向下取整,  剩下 4个, 不能被6号袋子 搞定,
                11            剩下 6个, 可以被搞定


错误的示范
奇数返回 -1
如果发现剩下的苹果数超过24,后边的袋子没有必要试

 */


func minBags(apple int) int {
	if apple < 0 {
		return -1
	}
	bag8 := apple >> 3
	rest := apple - (bag8 << 3)
	for bag8 >= 0 {
		if rest % 6 == 0 {
			return bag8 + (rest / 6)
		}else {
			bag8--
			rest += 8
		}
	}
	return -1
}

func minBags1(apple int) int  {
	if apple < 0 {
		return -1
	}
	bag6 := -1
	bag8 := apple / 8
	rest := apple - 8 * bag8
	for bag8 >= 0 && rest < 24 {
		restUse6 := minBagBase6(rest)
		if restUse6 != -1 {
			bag6 = restUse6
			break
		}
		bag8--
		rest = apple - 8 * (bag8)
	}
	if bag6 == -1 {
		return -1
	}
	return  bag6 + bag8
}


//如果剩余苹果reet 可以被装6个苹果的袋子搞定,返回袋子数量
//不能搞定返回-1
func minBagBase6(rest int) int {
	if rest % 6 == 0 {
		return rest / 6
	}
	return -1
}

func minBagAwesome(apple int) int {
	if apple & 1 != 0 { // 如果是奇数,返回-1
		return -1
	}
	if apple < 18 {
		if apple == 0 {
			return 0
		}
		if apple == 6 || apple == 8 {
			return 1
		}
		if apple == 12 || apple == 14 || apple == 16 {
			return 2
		}
		return -1
	}
	return (apple - 18) / 8 + 3
}

func TestApple(t *testing.T)  {
	for apple := 1; apple <= 100; apple++ {
		t.Log(apple,minBags(apple) == minBags1(apple))
	}
}







/*
题目二
给定一个正整数N,表示有N份青草统一堆放在仓库里
有一只牛和一只羊,牛先吃,羊后吃,它俩轮流吃草
不管是牛还是羊,每一轮能吃的草量必须是:
1,4,16,64... (4的某次方)
谁最先把草吃完谁获胜
假设牛和羊都决定聪明,都想赢,都会做出理性的决定
根据唯一的参数N,返回谁会赢
 */

//n份青草放在一堆
//先手后手都决定聪明
//string "先手" "后手"
func Winner1(n int) string  {
	//  0  1  2  3  4
	// 先 后  后  先 先
	if n < 5 {  // base case
		if n == 0 || n == 2 {
			return "后手"
		}
		return "先手"
	}

	// n >= 5时
	base := 1  // 当前先手决定吃的草数
	for base <= n {
		// 当前一共n份草,先手吃掉的是base份,n - base 是六个后手的
		// 母过程,先手在子过程里是后手
		if Winner1(n - base) == "后手" {
			return "先手"
		}
		if base > n / 4 {  // 防止 base * 4 溢出
			break
		}
		base *= 4
	}
	return "后手"
}

func Winner2(n int) string  {
	if n % 5 == 0 || n % 5 == 2 {
		return "后手"
	}
	return "先手"
}

func TestWinner(t *testing.T) {
	for i := 0; i <= 50; i++ {
		t.Log(i,":",Winner1(i) == Winner2(i))
	}
}



/*
题目三
定义一种数: 可以表示成若干(数量>1)连续正数和的数
比如:
5 = 2 + 3 5就是这样的数
12 = 3 + 4 + 5  12 就是这样的数
1不是这样的数,因为要求数量大于1个,连续正数和
2 = 1 + 1 2 也不是,因为等号右边不是连续正数
给定一个参数N,返回是不是可以表示成若干连续正数和的数
 */

func isSum1(num int) bool {
	for i := 1; i <= num; i++ {
		sum := i
		for j := i + 1; j <= num; j++ {
			if sum + j > num {
				break
			}
			if sum + j == num {
				return true
			}
			sum += j
		}
	}
	return false
}




func isSum2(num int) bool {
	if num < 3 {
		return false
	}
	
	// n是2的某次幂返回false
	return (num & (num - 1 )) != 0  // 等于0 是 2的某次方,不等于0 不是2的某次方
}




func TestIsSum(t *testing.T)  {
	for i := 1; i <= 1000000; i++ {
		if isSum1(i) != isSum2(i) {
			t.Fatal(isSum1(i),isSum2(i))
		}
	}
	t.Log("pass")
}

//数学题目起不到锻炼coding的目的,在1对1的面试场合中出现的越来越少
//在生产环境,可以写多个线程分布式的尝试,验证算法正确性
//话术:输入参数简单,输出参数简单,写暴力解,看规律,规律分析出来就可以直接用,至于有什么数学原理,再去想,不影响工作,工作之余再证明
// 连续M个正数和 原理




/*
数组  上有 开关的 状态    [T F T F F F T T]
先手: 决定在某个位置上选择后边全变的操作
后手: 从某个位置开始,后边0变1,1变0
赢,谁永远不会输
数学原理, 不管什么数组,看最后一个开关,如果是开着的,先手不可能赢,先手 不管做什么操作,都会改变最后一个开关的状态
 */




/*
打表
打表找规律

在一个 arr[]中有若干个数字, 每一个数有多少种质数因子,求质数因子的总和
arr 范围有限
[1:
[2:
做成一张表,这个表独立于程序之外
提前把表做好,做成全局的,可以省常数空间

不同于预处理结构,
比如前缀和数组
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值