首页
学习
活动
专区
圈层
工具
发布

微软的这个bug,2年都没有人发现~

刚看到个贴子,说微软确认Windows有个bug——“更新并关机”其实是“更新并重启”,整整两年没人发现。哈哈,这事吧,又离谱又合理。想想确实,大多数人电脑重启后都怀疑是自己点错了,或者系统抽风,根本不会想到是微软的问题。

网友们调侃说,这说明微软人浮于事,我倒觉得更像是“人性共谋”——大家都太习惯忍了。遇到点小毛病就想着凑合,能用就行。微软能混两年没被发现,也算得益于我们这种“算了”心态。

不过话说回来,大厂也该警醒了。信任不是天生的,是靠一次次“正常”积累出来的。一个小bug暴露的,不只是技术问题,还有企业的责任心。希望以后别再让用户替你“体谅”了。【备注:文末可领最新资料】

算法题:石子游戏

一排石堆,每堆有若干个石子,两个人轮流拿,每次只能拿最左或最右一堆,石子总数更多的人赢。这个题最容易掉坑的地方,是被“我拿这堆更大”这种局部直觉带偏了;真正要赢,得算清楚“我比对手多拿多少”,也就是分差思维。

问题建模与直觉

把 piles[i] 当成第 i 堆的石子数。设区间 [l, r] 还没拿。轮到我时,有两种选法:拿左边 l,或者拿右边 r。拿完之后,对手会在剩余区间里尽力让我的总分差变小。所以当前最优分差=我拿到的石子 − “对手在剩余区间里能拿到的分差”。这就把博弈递归成了区间 DP。

核心转移(区间 DP)

定义 dp[l][r]:在只剩 piles[l..r] 时,当前玩家相对对手的最大分差

拿左边:piles[l] − dp[l+1][r]

拿右边:piles[r] − dp[l][r-1] 于是:dp[l][r] = max(上述两者)。 边界:dp[i][i] = piles[i](只剩一堆,当前玩家全拿)。

最后答案看 dp[0][n-1] 是否 > 0,大于零说明先手能赢(先手比分差正)。

Java 实现

public class StoneGame {

  public boolean stoneGame(int[] piles) {

      int n = piles.length;

      int[][] dp = newint[n][n];

      // 长度为1的区间

      for (int i = 0; i < n; i++) dp[i][i] = piles[i];

      // 按区间长度扩展

      for (int len = 2; len <= n; len++) {

          for (int l = 0; l + len - 1 < n; l++) {

              int r = l + len - 1;

              int leftPick  = piles[l] - dp[l + 1][r];

              int rightPick = piles[r] - dp[l][r - 1];

              dp[l][r] = Math.max(leftPick, rightPick);

          }

      }

      return dp[0][n - 1] > 0;

  }

  // 小优化:O(n)空间的写法

  public boolean stoneGameSpaceOptimized(int[] piles) {

      int n = piles.length;

      int[] dp = newint[n]; // 表示上一层(r 维度)的分差

      for (int i = 0; i < n; i++) dp[i] = piles[i];

      for (int l = n - 2; l >= 0; l--) {

          for (int r = l + 1; r < n; r++) {

              int takeL = piles[l] - dp[r];

              int takeR = piles[r] - dp[r - 1];

              dp[r] = Math.max(takeL, takeR);

          }

      }

      return dp[n - 1] > 0;

  }

}

复杂度与几个顺嘴提醒

时间 O(n²),空间 O(n²),用一维滚动数组可降到 O(n)。数据范围大时要注意整型溢出(用 long)。如果题目改成“分差相等算平局”,就判断 dp[0][n-1] ≥ 0。

为啥常见版本“先手必胜”?

经典版(LeetCode 877)里堆数是偶数且每堆石子数为正,先手总能预定奇偶位(全拿奇数位或全拿偶数位),先看哪一侧总和更大,就通过第一步把对手“锁”到另一侧,后续无论对手怎么选,先手都能把剩下的目标位拿完。这是一个构造性证明;但在代码实现里,用上面的 DP 更通用,改规则也能顶得住。

就这样,别被每步“看起来更大的那堆”骗了,算清分差,你基本就稳了。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OrBPSwVvmPaWQRRUcZkQ7UZw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

领券