yanchang
yanchang
发布于 2026-06-10 / 27 阅读
0
0

代码起点,22年写下的第一个代码

碎碎念

今天翻旧文件,看到一段 2022 年刚接触编程时写的代码。

题目是蓝桥杯的《卡片》:0 到 9 每个数字各有 2021 张卡片,从 1 开始依次拼数字,拼过的数字不能拆,问最多能拼到多少。

这题本质上就是统计数字消耗。现在看,写个循环,把当前数字每一位拆出来扣库存,扣到不够为止就行。

但当年的我选择了另一条路:手搓。

原代码

#include <iostream>
using namespace std;
int main()
{
  int a[10]={2021,2021,2021,2021,2021,2021,2021,2021,2021,2021};
  for(int i=1;;i++)
  {
    int t=1;
    for(int j=0;j<10;j++)
    {
      if(a[j]==0)
      {
        t=0;
      }
    }
    if(t==0)
    {
      cout<<i-1<<endl;
      break;
    }
    else if (i<10)
    {
      a[i]--;
    }
    else if(i<100)
    {
      a[(i/10)%10]--;
      a[i%10]--;
    }
    else if(i<1000)
    {
      a[(i/100)%10]--;
      a[(i/10)%10]--;
      a[i%10]--;
    }
    else if(i<10000)
    {
      a[(i/1000)%10]--;
      a[(i/100)%10]--;
      a[(i/10)%10]--;
      a[i%10]--;
    }
    else if(i<100000)
    {
      a[(i/10000)%10]--;
      a[(i/1000)%10]--;
      a[(i/100)%10]--;
      a[(i/10)%10]--;
      a[i%10]--;
    }
  }
  return 0;
}

现在看哪里好笑

最有节目效果的是这几段:

else if(i<100)
{
  a[(i/10)%10]--;
  a[i%10]--;
}
else if(i<1000)
{
  a[(i/100)%10]--;
  a[(i/10)%10]--;
  a[i%10]--;
}

当时的思路非常朴素:一位数拆一位,两位数拆两位,三位数拆三位。

要是题目答案超过十万,那就继续复制粘贴:

a[(i/100000)%10]--;
a[(i/10000)%10]--;
a[(i/1000)%10]--;
a[(i/100)%10]--;
a[(i/10)%10]--;
a[i%10]--;

主打一个“位数增加了,但我的耐心也增加了”。

这段代码还有一个很神奇的判断:

for(int j=0;j<10;j++)
{
  if(a[j]==0)
  {
    t=0;
  }
}
if(t==0)
{
  cout<<i-1<<endl;
  break;
}

它不是判断当前数字 i 需要的卡片够不够,而是只要发现某种数字卡片刚好为 0,就直接停。

这在逻辑上其实不严谨。比如某个数字用完了,不代表下一个数一定需要这个数字;某个数字还剩 1 张,也不代表下一个数不会一次用 2 张。

但这段代码最后能跑出正确答案:3181

所以它属于那种“理论上有隐患,实践中刚好过了”的代码。竞赛填空题里,这种代码有时候反而很常见:不追求通用,不追求优雅,只要把答案跑出来就行。

如果现在写

现在写的话,我大概会直接拆数字:

#include <iostream>
using namespace std;

int main() {
  int cnt[10];
  for (int i = 0; i < 10; i++) cnt[i] = 2021;

  for (int x = 1; ; x++) {
    int need[10] = {0};
    int t = x;

    while (t > 0) {
      need[t % 10]++;
      t /= 10;
    }

    for (int d = 0; d < 10; d++) {
      if (need[d] > cnt[d]) {
        cout << x - 1 << endl;
        return 0;
      }
    }

    for (int d = 0; d < 10; d++) {
      cnt[d] -= need[d];
    }
  }
}

这版的区别很简单:

  • 不用关心数字是几位数;
  • 先判断够不够,再真正扣卡片;
  • 不会因为某个数字库存刚好为 0 就提前停;
  • 题目稍微改大一点,也不用继续复制粘贴。

结论

这段 2022 年的代码看起来确实很幽默。

它像是刚学会数组、循环、取模之后,把能用的东西全往题上招呼了一遍。代码不够通用,判断也有点冒险,但目标很明确:把答案算出来。

而且它真的算出来了。

答案:3181

从现在的角度看,它最大的问题不是“写得丑”,而是“刚好能过”。这种代码最容易给人一种错觉:我好像完全理解了。

其实只是题目比较给面子。


评论