***Định nghĩa trạng thái DP:***
- dp[i][j][0] là tổng lớn nhất khi xét đến phần tử thứ i, đã thực hiện tối đa j lần xóa và không thực hiện xóa ở vị trí hiện tại.
- dp[i][j][1] là tổng lớn nhất khi xét đến phần tử thứ i, đã thực hiện tối đa j lần xóa và có thể đang thực hiện một lần xóa.
***Công thức chuyển trạng thái:***
- Khi không xóa phần tử i:
dp[i][j][0]=max(dp[i−1][j][0]+A[i],dp[i−1][j][1]+A[i])
(Có thể đến đây từ trạng thái trước đó mà không xóa gì, hoặc từ trạng thái đang xóa trước đó).
- Khi bắt đầu hoặc đang thực hiện một lần xóa:
dp[i][j][1]=max(dp[i−1][j−1][0],dp[i−1][j][1])
(Có thể bắt đầu một lần xóa mới từ trạng thái trước đó hoặc tiếp tục xóa từ trạng thái đã xóa trước đó).
***Đáp án cuối cùng:***
- Kết quả là giá trị lớn nhất trong tất cả dp[n][j][0] và dp[n][j][1] với j thuộc [0,k].
***Độ phức tạp và bộ nhớ:***
- Số trạng thái cần tính: O(n×k)
- Thời gian tính một trạng thái: O(1)
- Độ phức tạp tổng: O(n×k)
- Bộ nhớ: O(n×k×2)
- **Vì n,k≤5000 nên sẽ không đủ bộ nhớ để lưu mảng dp[n][k][2]**
***Tối ưu bộ nhớ***
- Vì chúng ta chỉ sử dụng 2 trạng thái i và i−1 nên ta chỉ cần lưu 2 trạng thái đó
- Mảng dp mới sẽ là: dp[2][k][2]
- Bộ nhớ sau tối ưu: O(2×k×2)
***Code mẫu:***
/nhanzzzz−THPTXuanLoc#∈clude<bitsstdc++.h>#def∈e∫longlong#def∈eendl\n#def∈etaskmainusingnamespacestd;/const∫maxn=5005;∫n,k,arr[maxn],dp[2][maxn][2];sig≠dma∈(){ios::syncwithstdio(0);c∈.tie(0);cout.tie(0);if(fopen(task.inp,r)){eopen(task.inp,r,std∈);eopen(task.out,w,stdout);}/c∈⟩n⟩k;for(∫i=1;i≤n;i++){c∈⟩arr[i];}/dp[0][0][0]=dp[0][0][1]=0;for(∫i=1;i≤n;i++){if(i&1){dp[1][0][0]=dp[0][0][0]+arr[i];dp[1][0][1]=dp[0][0][1]+arr[i];for(∫j=1;j≤k;j++){dp[1][j][0]=max(dp[0][j][0]+arr[i],dp[0][j][1]+arr[i]);dp[1][j][1]=max(dp[0][j−1][0],dp[0][j][1]);}}else{dp[0][0][0]=dp[1][0][0]+arr[i];dp[0][0][1]=dp[1][0][1]+arr[i];for(∫j=1;j≤k;j++){dp[0][j][0]=max(dp[1][j][0]+arr[i],dp[1][j][1]+arr[i]);dp[0][j][1]=max(dp[1][j−1][0],dp[1][j][1]);}}}∫out=−9e18;for(∫j=0;j≤k;j++){out=max({out,dp[n%2][j][1],dp[n%2][j][0]});}cout⟨out;}