CF1923A

题解 · 2024-02-26

https://codeforces.com/contest/1923/problem/A

题意

一条彩带被分成 $n$ 个单元,从左到右依次编号为 $1$ 到 $n$ 。每个小格要么有一个芯片,要么是空闲的。
您可以执行以下任意次数(可能为零)的操作:选择一个筹码并将其移动到左边最近的空闲单元格。您可以选择任何您想要的芯片,只要它的左边至少有一个空闲的单元格。移动筹码时,操作前所在的单元格会变成空闲单元格。
您的目标是移动芯片,使它们形成一个单一的块,它们之间没有任何空闲单元格。您最少需要进行多少次操作?

题解

最左边筹码的位置记为 $l$ ,最右边筹码的位置记为 $r$ ,筹码数记为 $c$ 。

将所有的筹码都放在一个没有空位的区块中,意味着我们需要达到 $r - l = c - 1$ 时的情况。由于 $r - l \ge c - 1$ 总是满足的( $r-l = c-1$ 时的情况是当筹码尽可能靠近时),我们需要尽可能快地减小 $r-l$ 的值。

有两种方法可以做到这一点。一种是每次都尝试减小 $r$ ;也就是说,让我们编写一个贪婪的解决方案,总是对当前最右边的筹码进行操作。这实际上是问题的正确解决方案之一。

但如果我们证明了这一点,就能设计出更简单的解决方案。每当我们对最右边芯片以外的任何芯片进行操作时, $r-l$ 的值都不会减少(我们要么完全不改变 $l$ 和 $r$ ,要么减少 $l$ )。但是,每当我们对最右边的筹码进行运算时, $r$ 的值就会减少 $1$ (新的最右边的筹码将成为 $1$ )。(新的最右边的棋子将位于位置 $r-1$ --要么在操作之前就在那里,要么从 $r$ 移到那里)。因此,只对最右边的筹码进行运算会使 $r-l$ 减少,而且无论如何都会使 $r-l$ 恰好减少 $1$ 。因此,问题的答案实际上是 $(r-l) - (c-1)$ 。

#include <iostream>
using namespace std;
const int maxn = 55;
int t, s, l, r, n, a[maxn];
int main () {
  cin.tie (0)->sync_with_stdio (false);
  cin >> t;
  while (t--) {
    cin >> n;
    s = l = r = 0;
    for (int i = 1; i <= n; i++) {
      cin >> a[i];
      s += a[i];
      if (a[i]) r = i;
      if (a[i] && !l) l = i;
    }
    cout << r - l + 1 - s << '\n';
  }
  return 0;
}
Theme Jasmine by Kent Liao