[Gym - 103828I] Bombing buildings
Description
给定 1 1 1 个长度为 n n n 的数组 h h h,有 2 2 2 种类型的操作:
- 将位置 i i i 的数更改为 0 0 0,花费为 x x x
- 将区间 l ∼ r l\sim r l∼r 的所有数更改为 0 0 0,花费为 y y y,要求 h l ≤ h r , max i = l + 1 r − 1 ( h i ) < h l h_l\le h_r,\max_{i=l+1}^{r-1}(h_i)<h_l hl≤hr,maxi=l+1r−1(hi)<hl
求将整个序列全部改为 0 0 0 的最小代价。
Solution
发现对于区间 l ∼ r l\sim r l∼r,可以将 l ∼ r l\sim r l∼r 内部部分点更改为 0 0 0,使得满足区间更改为 0 0 0 的条件。所以,其实又划分为了若干个子问题,故确定该题为区间 DP。
思考 2 2 2 操作的进行是有条件的,所以不妨设 DP 数组表示满足该条件的最小代价,即 g l , r g_{l,r} gl,r 表示 l + 1 ∼ r l+1\sim r l+1∼r 所有数全部小于 h l h_l hl 的最小代价,特殊的 g i , i = 0 g_{i,i}=0 gi,i=0。
转移为:
g
l
,
r
=
{
g
l
,
r
−
1
h
l
>
h
r
g
l
,
i
+
f
i
+
1
,
r
i
∈
[
l
,
r
−
1
]
g_{l,r}= \begin{cases} & g_{l,r-1} & h_l>h_r \\ & g_{l,i}+f_{i+1,r} &i\in [l,r-1]\\ \end{cases}
gl,r={gl,r−1gl,i+fi+1,rhl>hri∈[l,r−1]
求出
g
g
g 之后,
f
l
,
r
=
g
l
,
r
−
1
f_{l,r}=g_{l,r-1}
fl,r=gl,r−1 就是将
l
+
1
∼
r
−
1
l+1\sim r-1
l+1∼r−1 全部都小于
h
l
h_l
hl 的代价,当然
f
l
,
r
=
min
(
f
l
,
k
+
f
k
+
1
,
r
,
f
l
,
r
)
f_{l,r}=\min(f_{l,k}+f_{k+1,r},f_{l,r})
fl,r=min(fl,k+fk+1,r,fl,r),即由两个全部赋为
0
0
0 的区间来更新,这是基本操作。
时间复杂度: O ( n 3 ) O(n^3) O(n3)
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define int long long
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 510;
int n, x, y;
int h[N], f[N][N], g[N][N];
int mx[N][N];
void solve() {
cin >> n >> x >> y;
for (int i = 1; i <= n; i ++)
cin >> h[i];
for (int i = 1; i <= n; i ++)
for (int j = i; j <= n; j ++)
mx[i][j] = max(mx[i][j - 1], h[j]);
memset(f, 0x3f, sizeof f);
memset(g, 0x3f, sizeof g);
for (int i = 1; i <= n; i ++) {
f[i][i] = x, g[i][i + 1] = (h[i] <= h[i + 1]) * x, g[i][i] = 0;
if (h[i] <= h[i + 1]) f[i][i + 1] = min(2 * x, y);
else f[i][i + 1] = 2 * x;
}
for (int len = 3; len <= n; len ++)
for (int l = 1; l <= n - len + 1; l ++) {
int r = l + len - 1;
if (h[l] > h[r]) g[l][r] = min(g[l][r], g[l][r - 1]);
for (int k = l; k < r; k ++)
g[l][r] = min(g[l][k] + f[k + 1][r], g[l][r]);
if (h[l] <= h[r]) f[l][r] = g[l][r - 1] + y;
for (int k = l; k < r; k ++)
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
}
cout << f[1][n] << endl;
}
signed main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int dt;
cin >> dt;
while (dt --)
solve();
return 0;
}