内鬼们の每日一题day5

内鬼们の每日一题day5

传送门CF448C

题意 :有n块连着的木板,每个木板的高度为 h i h_i hi,你需要把这 n n n块木板上色,每次上色你可以选择竖着刷完一块木板,或者横着刷一个单位高度的连续的木板,问最少需要刷几次。

1 ≤ n ≤ 5000 1 \leq n \leq 5000 1n5000 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

样例

输入#1

5
2 2 1 2 1

输出#1

3

输入#2

2
2 2

输出#2

2

有两种刷墙方式:横着、竖着

首先,如果需要横着刷,当前刷的位置的下面也必须横着刷。

在这里插入图片描述

考虑一般情况,(假设这里是要先横着刷)每次横着刷完一段连续的木板之后,会将整段木板分成许多不连续的木板段,于是就相当于得到规模更小的问题,

注意考虑如果高度很高,全部竖着涂(贡献 r - l + 1)也许会更优,

分治

code

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn = 5010;

int n;
ll a[maxn], ans = 0;

ll solve(int l, int r, int h){ //[l, r]区间内,最下面h已经被涂的贡献
//	cout << l << " " << r << " " << h << endl;
//	system("pause");
	
	if(l > n) return 0;
	if(l == r) return 1;
	
	ll mn = 2e9;
	for(int i = l; i <= r; ++ i){
		mn = min(mn, a[i]);
	}
	
	ll tmp = 0; //如果先横着的所有贡献
	tmp += mn - h; //先加上连续的横着涂的那段的贡献
	for(int i = l; i <= r; ++ i){
		if(a[i] == mn) continue; // 就不连续
		
		int j = i;
		for(; j < r; ++ j){
			if(a[j + 1] == mn) break; // 就不连续
		}
		tmp += solve(i, j, mn); //转化为更小的规模
		
		i = j + 1;
	}
	
	return min(tmp, (ll)r - l + 1); // 是先横着涂更优还是直接全部竖着涂更优
} 

int main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	//int n;
	cin >> n;
	for(int i = 1; i <= n; ++ i){
		cin >> a[i];
	}
	
	cout << solve(1, n, 0);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值