# 🌱 Lời giải Mely Educational Contest 04 bài H : Rừng cây
>[color=pink] Nếu bạn cảm thấy Edit này chất lượng tiếc gì một upvote và giới thiệu team với mọi người nhé :heart:
###### 📋 Content:
[TOC]
____
## Nhận xét :label:
:question: Với trường hợp **K = 0**, ta có nhận xét : đáp án sẽ là tổng tất cả **max(0,h[i]-h[i-1])** với *(**1** <= **i** <= **n**)* và **h[0] = 0**; chứng minh nhận xét này không khó nên mình sẽ để dành cho các bạn suy nghĩ :D
Với nhận xét trên ta có thể nghĩ tới một thuật toán quy hoạch động như sau :
>[color=green]
>+ gọi ***dp[i][t]*** là đến với vị trí thứ ***i*** (cây thứ ***i*** không thay đổi độ cao) và số cây thay đổi là ***t*** thì phải tốn ít nhất trong bao nhiêu sức mạnh
>+ Giả sử hai cột không thay đổi giá trị liên tiếp là ***j*** và ***i*** thì dễ nhận thấy các cây nằm giữa ***j*** và ***i*** sẽ chuyển hết độ cao về ***h[j]*** hoặc ***h[i]*** do nhận xét ở trên đã trình bày. Nói cách khác ta có thể coi như bỏ qua các nằm giữa ***i*** và ***j***. Từ đây ta có cách chuyển đổi trạng thái :
>+ ***dp[i][t] = max(dp[i][t], dp[j][t-(i-j-1)] + max(0,h[i]-h[j]) )***
>+ Đáp án là ***max(dp[i][t])*** mà ***(t + n – i <= k)***.
Thuật toán này có thể giải dễ dàng với độ phức tạp $O(n^3)$, tuy nhiên để tối ưu xuống $O(n^2log)$ ta cần nhận xét thêm :
>[color=green]
>- ***dp[i][t]*** sẽ được cập nhật từ các trạng thái ***dp[x][y]*** mà **x-y = i-t-1** (giống đường chéo trên bảng); vậy nên các trạng thái này hoàn toàn có thể cập nhật nhanh bằng các lập thêm mảng phụ . Tuy nhiên do còn điều kiện ***max(0,h[i]-h[x])*** do đó chúng ta cần đến ctdl cùng kĩ thuật nén số để kiểm soát 2 trường hợp (mỗi trường hợp dùng 1 cây phân đoạn -> mỗi đường chéo cần xét bằng 2 cây phân đoạn tương ứng) :
>- Nếu ***(h[x] <= h[i])*** thì ***dp[i][t] = (dp[x][y] – h[x])*** + ***h[i]***) -> cây phân đoạn TH1 này sẽ dùng để lấy min các giá trị ***(dp[x][y] – h[x])*** mà ***h[x] <= h[i]***.
>- Nếu ***(h[x] > h[i])*** ***dp[i][t] = dp[x][y] + h[i])*** -> cây phân đoạn TH2 này sẽ dùng để lấy min các giá trị ***(dp[x][y] – h[x])*** mà ***(h[x] > h[i]).***
## Code mẫu :bulb:
> **Time:** $O(n^2log)$
> **Space:** $O(n^2)$
> **Algo:** Dynamic programing.
> [color=lightgreen]
:::success
:::spoiler code mẫu
``` cpp
#include<iostream>
#include<vector>
#include<algorithm>
#pragma GCC target ("avx2")
#pragma GCC optimization ("O3")
#pragma GCC optimization ("unroll-loops")
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
//#define int long long
using namespace std;
using ll = long long;
const int maxN = 2e3 + 5;
const int mod = 998244353;
const ll infty = 1e18;
int n,k;
ll dp[2005][2005];
vector<int> unq;
int h[maxN];
struct Segtree
{
ll st[4*maxN];
void Init()
{
fill(st,st + 4*n + 5,infty);
}
void Update(int idx,int l,int r,int i,ll val)
{
if(i < l || i > r) return;
if(l == r)
{
st[idx] = min(st[idx],val);
return;
}
int mid = (l + r) / 2;
Update(idx * 2,l,mid,i,val);
Update(idx * 2 + 1,mid+1,r,i,val);
st[idx] = min(st[idx*2],st[idx*2+1]);
}
ll Get(int idx,int l,int r,int u,int v)
{
if(v < l || u > r) return infty;
if(u <= l && r <= v)
return st[idx];
int mid = (l + r) / 2;
return min(Get(idx*2,l,mid,u,v),Get(idx*2+1,mid+1,r,u,v));
}
};
Segtree small[2005],big[2005];
inline int new_val(int x)
{
return upper_bound(unq.begin(),unq.end(),x) - unq.begin();
}
void Read()
{
cin >> n >> k;
for(int i = 1;i <= n;i++)
{
cin >> h[i];
unq.push_back(h[i]);
}
for(int i = 0;i <= n;i++)
{
small[i].Init();
big[i].Init();
for(int j = 0;j <= k;j++)
{
dp[i][j] = 1e18;
}
}
unq.push_back(0);
sort(unq.begin(),unq.end());
unq.erase(unique(unq.begin(),unq.end()),unq.end());
dp[0][0] = 0;
small[0].Update(1,1,n+1,new_val(0),0);
big[0].Update(1,1,n+1,new_val(0),0);
for(int i = 1;i <= n;i++)
{
dp[i][0] = dp[i-1][0] + max(0,h[i] - h[i-1]);
small[i].Update(1,1,n+1,new_val(h[i]),dp[i][0]-h[i]);
big[i].Update(1,1,n+1,new_val(h[i]),dp[i][0]);
}
for(int j = 1;j <= k;j++)
{
for(int i = 0;i <= n;i++)
{
//dp[i][j] = min(dp[i][j],dp[i][j-1]);
if(i - 1 < j) continue;
int x = new_val(h[i]);
dp[i][j] = min(small[i-j-1].Get(1,1,n+1,1,x) + h[i],dp[i][j]);
dp[i][j] = min(big[i-j-1].Get(1,1,n+1,x+1,n+1),dp[i][j]);
small[i-j].Update(1,1,n+1,x,dp[i][j]-h[i]);
big[i-j].Update(1,1,n+1,x,dp[i][j]);
}
}
ll res = 1e18;
for(int j = 0;j <= k;j++)
{
for(int i = 0;i <= n;i++)
{
if(j + (n - i) <= k)
{
res = min(res,dp[i][j]);
}
}
}
cout << res;
}
void Solve()
{
}
void Debug()
{
}
int32_t main()
{
//freopen("32.inp","r",stdin);
//freopen("test.out","w",stdout);
ios_base::sync_with_stdio(0);
cin.tie(0);
// int subtask;
int tt;
tt = 1;
//cin >> tt;
for(int i = 1; i <= tt; i++)
{
Read();
Solve();
Debug();
}
}
```
:::success