Shift + Esc

以下内容从 开始标号。

一股浓烈的 ABC 味扑面而来。类似最小化 的形式已经在 ABC 的 F 题中欧出现好多次了。

我们不关心最终答案里具体进行了多少次旋转操作,状态仅需设置 表示当前位于第 行第 列的最小代价。

我们以行为单位进行转移。对于单独的一行,我们需要考虑进行多少次旋转操作。因此,可以设 表示当前位于第 行第 列,第 行进行了 次旋转操作。

转移时,我们可以选择从上方过来,也可以选择从左侧过来。对第 列进行 次旋转后,格子 上的数变成了 ,同时会导致代价增加 。于是我们有转移方程:

完成一整行后,我们接着更新该行的 。显然每列的答案为所有位移方案中该列的最小者。

时间复杂度

#include <cstddef>
#include <iostream>
#include <limits>
#include <numeric>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
int main(void) {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  size_t T;
  fin >> T;
  while (T--) {
    size_t n, m;
    ui k;
    fin >> n >> m >> k;
    vector<uli> f(m, numeric_limits<uli>::max() / 2);
    f[0] = 0;
    for (size_t c = 0; c < n; ++c) {
      vector<uli> a(m);
      for (uli& i : a) fin >> i;
      vector g(m, vector<uli>(m));
      for (size_t t = 0; t < m; ++t) {
        g[t][0] = f[0] + (uli)k * t + a[(t + 0) % m];
        for (size_t i = 1; i < m; ++i)
          g[t][i] = min(g[t][i - 1], f[i] + (uli)k * t) + a[(t + i) % m];
      }
      f = vector<uli>(m, numeric_limits<uli>::max() / 2);
      for (auto const& i : g)
        for (size_t j = 0; j < m; ++j) f[j] = min(f[j], i[j]);
    }
    fout << f.back() << '\n';
  }
  return 0;
}
posted @ 2024-12-22 16:05  MrPython  阅读(9)  评论(0)    收藏  举报  来源