【数学】D:石子游戏 | 2021牛客寒假算法基础集训营 5

石子游戏

https://ac.nowcoder.com/acm/contest/9985/D

补充

CSDN 阅读体验更佳:【训练题35:数学】D:石子游戏 | 2021牛客寒假算法基础集训营 5

【题意】D:石子游戏 | 2021牛客寒假算法基础集训营 5

  • 一排,有 个石头堆,第 堆有 个石头。
    给你一个
    你每次可以选择相邻的 个石头堆,进行一次操作,这 堆,每堆石头个数都会**增加**
    问你最少操作个数使得每堆石头个数都相同,或者输出 表示不可能。

    【范围】

  • 样例组数

    【思路】赛内

  • 我感觉不是直接二分+贪心就能做好的,因为假设最后每堆石头个数都是
    可能 是可以达成的,但是 都是无法达成的,比较棘手。
    于是我就对 进行了分析:
    例子:
    我们已经假设最终每堆都有 个石头了,那么由于第一堆增加到 的方案只有一种,就是选择增加 堆,每堆增加 个,于是变成了:重复下去。第二堆石头需要增加 个,增加方案也只有一种,就是 堆每堆增加 个。
    我们从头到尾来一遍:可以看到,有解的情况就是 ,即**剩下下标从 的所有数字都相同。**
    当然还有一些特殊情况,比如:这样我们操作完之后,。这个时候,我们的解就是原来序列的元素的最大值
    我们得到了 之后,就能知道增加了多少个石头,然后每次操作是增加 个,做个除法就行。
  • 那么问题来了,我们代码里面怎么去设自变量 ?
    额,随便给一个大的常量就行了。 给小常量是不行的,因为这样比如可能导致上面例子中的 ,然后差分增加数字会比较麻烦。
  • 我怎么知道当前位置为 还是某个数字增加了一些值后的新值?
    可以看到,一定是每 个数字一起变成 的,我们记录一下 表示第一位非 的位置即可。

    【代码】

  • 时间复杂度: 记得用差分处理区间增加
    _            __   __          _          _
    | |           \ \ / /         | |        (_)
    | |__  _   _   \ V /__ _ _ __ | |     ___ _
    | '_ \| | | |   \ // _` | '_ \| |    / _ \ |
    | |_) | |_| |   | | (_| | | | | |___|  __/ |
    |_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
          __/ |
         |___/
    const int MAX = 1e5+50;
    ll aa[MAX];
    ll de[MAX];
    ll aim = (ll)2e9;
    int main()
    {
      int T;scanf("%d",&T);
      while(T--){
          ll mx = 0;
          int n,k;scanf("%d%d",&n,&k);
          for(int i = 0;i <= n + 1;++i)de[i] = 0;
          for(int i = 1;i <= n;++i){
              scanf("%lld",&aa[i]);
              mx = max(mx,aa[i]);
          }
          bool can = true;
          ll now = 0;
          int last = 0;
          for(int i = 1;i + k - 1 <= n;++i){        // 算前面的区间都要变成 aim
              now += de[i];
              ll shu = aa[i] + now;
              if(shu > aim){
                  can = false;
                  break;
              }else if(shu < aim){
                  de[i+1] += aim - shu;
                  de[i+k] -= aim - shu;
                  if(i > last)
                      last = i + k - 1;
              }
          }
          ll ans = -1;
          if(last){
              for(int i = n - k + 2;i <= last;++i){        // 这些数字必须都是 aim
                  now += de[i];
                  ll shu = aa[i] + now;
                  if(shu != aim){
                      can = false;
                      break;
                  }
              }
              for(int i = last + 1;i <= n;++i){            // 这些都是常数,常数都要相同
                  now += de[i];
                  ll shu = aa[i] + now;
                  if(ans == -1)ans = shu;
                  else if(ans != shu){
                      can = false;
                      break;
                  }
              }
          }
          if(!can)puts("-1");
          else {
              if(ans == -1)ans = mx;
              ll cha = 0;
              for(int i = 1;i <= n;++i){
                  cha += ans - aa[i];
              }
              cha /= k;
              printf("%lld\n",cha);
          }
      }
      return 0;
    }
全部评论

相关推荐

上周组里招人,我面了六个候选人,回来跟同事吃饭的时候聊起一个让我挺感慨的现象。前三个候选人,算法题写得都不错。第一道二分查找,五分钟之内给出解法,边界条件也处理得干净。第二道动态规划,状态转移方程写对了,空间复杂度也优化了一版。我翻他们的简历,力扣刷题量都在300以上。后三个呢,就有点参差不齐了。有的边界条件没处理好,有的直接说这道题没刷过能不能换个思路讲讲。其中有一个女生,我印象特别深——她拿到题之后没有马上写,而是先问我:“面试官,我能先跟你确认一下我对题目的理解吗?”然后她把自己的思路讲了一遍,虽然最后代码写得不是最优解,但整个沟通过程非常顺畅。这个女生的代码不是最优的,但当我问她“如果这里是线上环境,你会怎么设计’的时候,她给我讲了一套完整的方案——异常怎么处理、日志怎么打、怎么平滑发布。她对这是之前在实习的时候踩过的坑。”我在想LeetCode到底在筛选什么?我自己的经历可能有点代表性。我当年校招的时候,也是刷了三百多道题才敢去面试。那时候大家都刷,你不刷就过不了笔试关。后来工作了,前三年基本没再打开过力扣。真正干活的时候,没人让你写反转链表,也没人让你手撕红黑树。更多的是:这个接口为什么慢了、那个服务为什么OOM了、线上数据对不上了得排查一下。所以后来我当面试官,慢慢调整了自己的评判标准。算法题我还会出,但目的变了。我出算法题,不是想看你能不能背出最优解。而是想看你拿到一个陌生问题的时候,是怎么思考的。你会先理清题意吗?你会主动问边界条件吗?你想不出来的时候会怎么办?你写出来的代码,变量命名乱不乱、结构清不清楚?这些才是工作中真正用得到的能力。LeetCode是一个工具,不是目的。它帮你熟悉数据结构和常见算法思路,这没问题。但如果你刷了三百道题,却说不清楚自己的项目解决了什么问题、遇到了什么困难、你是怎么解决的,那这三百道题可能真的白刷了。所以还要不要刷LeetCode?要刷,但别只刷题。刷题的时候,多问自己几个为什么:为什么用这个数据结构?为什么这个解法比那个好?如果换个条件,解法还成立吗?把刷题当成锻炼思维的方式,而不是背答案的任务。毕竟面试官想看到的,从来不是一台背题机器,而是一个能解决问题的人。
牛客51274894...:意思是光刷力扣还不够卷
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务