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:
做成一张表,这个表独立于程序之外
提前把表做好,做成全局的,可以省常数空间
不同于预处理结构,
比如前缀和数组
*/