P3062

· · 题解

解题思路:

首先观察题面,不难发现,对于任意一段区间 [i, j],若已知 [1, i-1] 都被覆盖的最小代价时,是可以直接求出覆盖 [1, j] 的最小代价的。所以我们不难想到区间动态规划,而这题很明显可以用到收集型的优化。

于是我们令 cost_i 表示以 i 结尾的区间的最小代价,而 f_{i ,j} 表示以区间 [1, i] 从 j 断开,分成 [1, j - 1][j, i] 两个区间,我们可以写出状态转移方程:

f_{i, j} = max(f_{i, j}, cost_j - 1 + money_{j, i})

此处 money_{j, i} 算的是在区间 [i, j] 中放一个信号发射器的最小代价。然后根据这个转移方程我们就可以写出代码了。

代码如下

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int kMaxN = 2001;

int n, w[kMaxN], f[kMaxN][kMaxN], cost[kMaxN], a, b;

int main() {
  ios_base::sync_with_stdio(0), cin.tie(NULL), cout.tie(NULL);
  cin >> n >> a >> b;
  for (int i = 1; i <= n; i++) {
    cin >> w[i];
    cost[i] = (1e9 + 1);//cost计算[1,i]的最小代价
  }
  sort(w + 1, w + n + 1);
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
      f[j][i] = cost[j - 1] + (w[i] - w[j]) * b + 2 * a;
      cost[i] = min(cost[i], f[j][i]);
    }
  }
  if (cost[n] % 2) {
    cout << cost[n] / 2 << ".5";
  } else {
      cout << cost[n] / 2;
  }
  return 0;
}

虽然这一段代码也能通过,但经过我反复的 看题解 推敲后,我想到了优化状态的方式。

我们发现实际上 f 数组是废的,我们可以直接删掉,因为我们只需要求所有的可以表示为 [1, i] 的区间,而 f 数组也是同样的作用,因此此时我们的状态转移方程可以简化为:

cost_i = min(cost_{j-1} + money_{i,j}) 最后同样记得要乘 2 处理,因为题目是要求你在答案为整数时不输出小数部分。 ```cpp #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int kMaxN = 2001; int n, w[kMaxN], cost[kMaxN], a, b; int main() { ios_base::sync_with_stdio(0), cin.tie(NULL), cout.tie(NULL); cin >> n >> a >> b; for (int i = 1; i <= n; i++) { cin >> w[i]; cost[i] = (1e9 + 1);//cost计算[1,i]的最小代价 } sort(w + 1, w + n + 1); for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { cost[i] = min(cost[j - 1] + (w[i] - w[j]) * b + 2 * a, cost[i]); } } if (cost[n] % 2) { cout << cost[n] / 2 << ".5"; } else { cout << cost[n] / 2; } return 0; } ``` 完结撒花~~~