根据对数器找规律、根据数据量猜解法

97 阅读6分钟

对数器找规律

1)某个面试题,输入参数类型简单,并且只有一个实际参数

2)要求的返回值类型也简单,并且只有一个

3)用暴力方法,把输入参数对应的返回值,打印出来看看,进而优化code

题目一

小虎去买苹果,商店只提供两种类型的塑料袋,每种类型都有任意数量。

1)能装下6个苹果的袋子

2)能装下8个苹果的袋子

小虎可以自由使用两种袋子来装苹果,但是小虎有强迫症,他要求自己使用的袋子数量必须最少,且使用的每个袋子必须装满。

给定一个正整数N,返回至少使用多少袋子。如果N无法让使用的每个袋子必须装满,返回-1

public static int minBags(int apple) {
   if (apple < 0) {
      return -1;
   }
   int bag8 = (apple >> 3);
   int rest = apple - (bag8 << 3);
   while(bag8 >= 0) {
      // rest 个
      if(rest % 6 ==0) {
         return bag8 + (rest / 6);
      } else {
         bag8--;
         rest += 8;
      }
   }
   return -1;
}

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

题目二

给定一个正整数N,表示有N份青草统一堆放在仓库里

有一只牛和一只羊,牛先吃,羊后吃,它俩轮流吃草

不管是牛还是羊,每一轮能吃的草量必须是:1,4,16,64…(4的某次方)

谁最先把草吃完,谁获胜

假设牛和羊都绝顶聪明,都想赢,都会做出理性的决定

根据唯一的参数N,返回谁会赢

// 如果n份草,最终先手赢,返回"先手"
// 如果n份草,最终后手赢,返回"后手"
public static String whoWin(int n) {
   if (n < 5) {
      return n == 0 || n == 2 ? "后手" : "先手";
   }
   // 进到这个过程里来,当前的先手,先选
   int want = 1;
   while (want <= n) {
      if (whoWin(n - want).equals("后手")) {
         return "先手";
      }
      if (want <= (n / 4)) {
         want *= 4;
      } else {
         break;
      }
   }
   return "后手";
}

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

题目三

定义一种数:可以表示成若干(数量>1)连续正数和的数

比如:

5 = 2+3,5就是这样的数

12 = 3+4+5,12就是这样的数

1不是这样的数,因为要求数量大于1个、连续正数和

2 = 1 + 1,2也不是,因为等号右边不是连续正数

给定一个参数N,返回是不是可以表示成若干连续正数和的数

    public static boolean isMSum1(int num) {
      for (int start = 1; start <= num; start++) {
         int sum = start;
         for (int j = start + 1; j <= num; j++) {
            if (sum + j > num) {
               break;
            }
            if (sum + j == num) {
               return true;
            }
            sum += j;
         }
      }
      return false;
   }

   public static boolean isMSum2(int num) {
//    
//    return num == (num & (~num + 1));
//    
//    return num == (num & (-num));
//    
//    
      return (num & (num - 1)) != 0;
   }

根据数据规模猜解法

1)C/C++,1秒处理的指令条数为10的8次方

2)Java等语言,1~4秒处理的指令条数为10的8次方

3)这里就有大量的空间了!

题目四

int[] d,d[i]:i号怪兽的能力

int[] p,p[i]:i号怪兽要求的钱

开始时你的能力是0,你的目标是从0号怪兽开始,通过所有的怪兽。

如果你当前的能力,小于i号怪兽的能力,你必须付出p[i]的钱,贿赂这个怪兽,然后怪兽就会加入你,他的能力直接累加到你的能力上;如果你当前的能力,大于等于i号怪兽的能力,你可以选择直接通过,你的能力并不会下降,你也可以选择贿赂这个怪兽,然后怪兽就会加入你,他的能力直接累加到你的能力上。

返回通过所有的怪兽,需要花的最小钱数。

// int[] d d[i]:i号怪兽的武力
// int[] p p[i]:i号怪兽要求的钱
// ability 当前你所具有的能力
// index 来到了第index个怪兽的面前

// 目前,你的能力是ability,你来到了index号怪兽的面前,如果要通过后续所有的怪兽,
// 请返回需要花的最少钱数
public static long process1(int[] d, int[] p, int ability, int index) {
   if (index == d.length) {
      return 0;
   }
   if (ability < d[index]) {
      return p[index] + process1(d, p, ability + d[index], index + 1);
   } else { // ability >= d[index] 可以贿赂,也可以不贿赂
      return Math.min(

            p[index] + process1(d, p, ability + d[index], index + 1),

            0 + process1(d, p, ability, index + 1));
   }
}

public static long func1(int[] d, int[] p) {
   return process1(d, p, 0, 0);
}

// 从0....index号怪兽,花的钱,必须严格==money
// 如果通过不了,返回-1
// 如果可以通过,返回能通过情况下的最大能力值
public static long process2(int[] d, int[] p, int index, int money) {
   if (index == -1) { // 一个怪兽也没遇到呢
      return money == 0 ? 0 : -1;
   }
   // index >= 0
   // 1) 不贿赂当前index号怪兽
   long preMaxAbility = process2(d, p, index - 1, money);
   long p1 = -1;
   if (preMaxAbility != -1 && preMaxAbility >= d[index]) {
      p1 = preMaxAbility;
   }
   // 2) 贿赂当前的怪兽 当前的钱 p[index]
   long preMaxAbility2 = process2(d, p, index - 1, money - p[index]);
   long p2 = -1;
   if (preMaxAbility2 != -1) {
      p2 = d[index] + preMaxAbility2;
   }
   return Math.max(p1, p2);
}

public static int minMoney2(int[] d, int[] p) {
   int allMoney = 0;
   for (int i = 0; i < p.length; i++) {
      allMoney += p[i];
   }
   int N = d.length;
   for (int money = 0; money < allMoney; money++) {
      if (process2(d, p, N - 1, money) != -1) {
         return money;
      }
   }
   return allMoney;
}

public static long func2(int[] d, int[] p) {
   int sum = 0;
   for (int num : d) {
      sum += num;
   }
   long[][] dp = new long[d.length + 1][sum + 1];
   for (int i = 0; i <= sum; i++) {
      dp[0][i] = 0;
   }
   for (int cur = d.length - 1; cur >= 0; cur--) {
      for (int hp = 0; hp <= sum; hp++) {
         // 如果这种情况发生,那么这个hp必然是递归过程中不会出现的状态
         // 既然动态规划是尝试过程的优化,尝试过程碰不到的状态,不必计算
         if (hp + d[cur] > sum) {
            continue;
         }
         if (hp < d[cur]) {
            dp[cur][hp] = p[cur] + dp[cur + 1][hp + d[cur]];
         } else {
            dp[cur][hp] = Math.min(p[cur] + dp[cur + 1][hp + d[cur]], dp[cur + 1][hp]);
         }
      }
   }
   return dp[0][0];
}

public static long func3(int[] d, int[] p) {
   int sum = 0;
   for (int num : p) {
      sum += num;
   }
   // dp[i][j]含义:
   // 能经过0~i的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少?
   // 如果dp[i][j]==-1,表示经过0~i的怪兽,花钱为j是无法通过的,或者之前的钱怎么组合也得不到正好为j的钱数
   int[][] dp = new int[d.length][sum + 1];
   for (int i = 0; i < dp.length; i++) {
      for (int j = 0; j <= sum; j++) {
         dp[i][j] = -1;
      }
   }
   // 经过0~i的怪兽,花钱数一定为p[0],达到武力值d[0]的地步。其他第0行的状态一律是无效的
   dp[0][p[0]] = d[0];
   for (int i = 1; i < d.length; i++) {
      for (int j = 0; j <= sum; j++) {
         // 可能性一,为当前怪兽花钱
         // 存在条件:
         // j - p[i]要不越界,并且在钱数为j - p[i]时,要能通过0~i-1的怪兽,并且钱数组合是有效的。
         if (j >= p[i] && dp[i - 1][j - p[i]] != -1) {
            dp[i][j] = dp[i - 1][j - p[i]] + d[i];
         }
         // 可能性二,不为当前怪兽花钱
         // 存在条件:
         // 0~i-1怪兽在花钱为j的情况下,能保证通过当前i位置的怪兽
         if (dp[i - 1][j] >= d[i]) {
            // 两种可能性中,选武力值最大的
            dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]);
         }
      }
   }
   int ans = 0;
   // dp表最后一行上,dp[N-1][j]代表:
   // 能经过0~N-1的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少?
   // 那么最后一行上,最左侧的不为-1的列数(j),就是答案
   for (int j = 0; j <= sum; j++) {
      if (dp[d.length - 1][j] != -1) {
         ans = j;
         break;
      }
   }
   return ans;
}