EOJ 2799 区间覆盖

在网上搜索这个题目,几乎全是那个用贪心做的版本……

这题用的是 dp。重点在降复杂度的思维上:用一个 w[i][j] 数组记录完全覆盖区间 i..=j 的最小花销。不过这个定义是不严谨的,因为如果 w[1][3] != kInf 并且 w[4][5] != kInfw[1][5] 还是可能等于 kInf。但是看按步 dp 的时候这个小问题对正确性没有影响。

详细的解释在代码注释里啦。

#include "bits/stdc++.h"
using namespace std;
using u32 = uint32_t;
using u64 = uint64_t;
/******************************************************************************/

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);

  u32 t;
  cin >> t;
  for (u32 _ = 0; _ != t; ++_) {
    cout << "case #" << _ << ":\n";

    u32 n, m, k;
    cin >> n >> m >> k;
    u64 constexpr static kMaxWeight = 1000000000;
    u64 constexpr static kInf = numeric_limits<u64>::max() / 2 - kMaxWeight;
    vector<vector<u64>> w(n + 1, vector<u64>(n + 1, kInf));
    vector<vector<u64>> dp(n + 1, vector<u64>(n + 1, kInf));
    for (auto& i : dp) {
      i[0] = 0;
    }

    for (u32 i = 0; i != m; ++i) {
      u32 l, r, cost;
      cin >> l >> r >> cost;
      for (u32 j = l, j_end = r + 1; j != j_end; ++j) {
        if (w[j][r] > cost) {
          w[j][r] = cost;
        }
      }
    }

    for (u32 i = 1, i_end = n + 1; i != i_end; ++i) {
      for (u32 j = 1; j != i; ++j) {
        dp[i][j] = dp[i - 1][j];
        for (u32 u = 1, u_end = j + 1; u != u_end; ++u) {
          auto tmp = dp[i - u][j - u] + w[i - u + 1][i];
          if (dp[i][j] > tmp) {
            dp[i][j] = tmp;
          }
        }
      }
    }

    // find the answer, since the question asks for the `at least` condition
    // CAVEAT: Dereferencing `.end()` may cause runtime error in VS debug mode.
    //         If so, try `&dp[n].back() + 1`.
    auto ans = *min_element(&dp[n][k], &*dp[n].end());
    if (ans == kInf) {
      cout << "-1\n";
    } else {
      cout << ans << '\n';
    }
  }
}
posted @ 2020-10-05 10:16  seideun  阅读(182)  评论(0)    收藏  举报