【动态规划题目讲解】[Gym - 103828I] Bombing buildings

[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 lr 的所有数更改为 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 hlhr,maxi=l+1r1(hi)<hl

求将整个序列全部改为 0 0 0 的最小代价。


Solution

发现对于区间 l ∼ r l\sim r lr,可以将 l ∼ r l\sim r lr 内部部分点更改为 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+1r 所有数全部小于 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,r1gl,i+fi+1,rhl>hri[l,r1]
求出 g g g 之后, f l , r = g l , r − 1 f_{l,r}=g_{l,r-1} fl,r=gl,r1 就是将 l + 1 ∼ r − 1 l+1\sim r-1 l+1r1 全部都小于 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值