Atcoder abc 345
E
问题陈述
有 \(N\) 个球排成一排。
左边的 \(i\) 个球的颜色是 \(C_i\) ,数值是 \(V_i\) 。
高桥希望在不改变顺序的情况下,将这一行中的 \(K\) 个球完全删除,这样在排列剩余的球时,就不会有相邻的两个球颜色相同。此外,在此条件下,他希望最大化这一行中剩余球的总价值。
求高桥是否能移除 \(K\) 个球,使剩余一行中没有相邻的两个球颜色相同。如果可以,求剩余球的最大总值。
不难想到暴力的 \(dp\) , \(dp[i][j][k]\) 代表前 \(i\) 个数中,已经删除了 \(j\) 个数,并且以颜色 \(k\) 结尾的最大值。
将第一维滚动一下,能做到时间复杂度 \(O(N^2K)\) , 空间复杂度 \(O(NK)\)。
#include <bits/stdc++.h>
using ll = long long;
const int N = 2E5 + 1 , K = 501;
const int inf = 1E9;
int dp[2][K][N];
void solve() {
int n,m;
std::cin >> n >> m;
std::vector<int> c(n + 1) , v(n + 1);
for (int i = 1 ; i <= n ; ++i) {
std::cin >> c[i] >> v[i];
}
int flag = 0;
for (int j = 0 ; j <= m ; ++j) {
for (int k = 0 ; k <= n ; ++k) {
dp[0][j][k] = -inf;
}
}
dp[0][0][0] = 0;
for (int i = 1 ; i <= n ; ++i) {
flag ^= 1;
for (int j = 0 ; j <= m ; ++j) {
for (int k = 0 ; k <= n ; ++k) {
dp[flag][j][k] = -inf;
}
}
for (int j = 0 ; j <= m ; ++j) {
for (int k = 0 ; k <= n ; ++k) {
if (c[i] == k) {
dp[flag][j + 1][k] = std::max(dp[flag][j + 1][k] , dp[flag ^ 1][j][k]);
} else {
dp[flag][j + 1][k] = std::max(dp[flag][j + 1][k] , dp[flag ^ 1][j][k]);
dp[flag][j][c[i]] = std::max(dp[flag][j][c[i]] , dp[flag ^ 1][j][k] + v[i]);
}
}
}
}
int mx = -1;
for (int k = 1 ; k <= n ; ++k) {
mx = std::max(mx , dp[flag][m][k]);
}
std::cout << mx << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
正解的话学习了 \(jiangly\) 的思路。
\(dp[i][k]\) 代表着前 \(i\) 个中删了 \(k\) 个,并且第 \(i\) 个球必定作为结尾的最大值。
这里将第 \(i\) 个球必定入选作为了条件,简化了暴力状态,应为答案肯定是某个球作为结尾。
- 我们发现 \(dp[i][k]\) 的候选集合就是 \(dp[i - 1][k] , dp[i - 2][k - 1] , dp[i - 3][k - 2] , ...\)
只要 $c[i - 1] , c[i - 2] , c[i - 3] , ... $ 颜色与 \(c[i]\) 不同就能去更新 \(dp[i][k]\). - 那么同理,\(dp[i - 1][k - 1]\) 的候选集合与 \(dp[i][k]\) 的候选集合仅差 \(dp[i - 1][k]\)
3.这样就启示我们每次去更新一个长度 \(k\) 的序列 \(dp\) , 类似于\(dp[1][0] , dp[2][1] , dp[3][2]..\), 在这期间添加\(dp[i - 1][k]\), 并同时维护两个颜色不同的最大值即可。
#include <bits/stdc++.h>
using LL = long long;
using namespace std;
const LL inf = 1E18;
void solve() {
int n , k;
cin >> n >> k;
vector<int> c(n + 2) , v(n + 2);
for (int i = 1 ; i <= n ; ++i) {
cin >> c[i] >> v[i];
}
c[0] = n + 1;
c[n + 1] = n + 2;
n += 2;
vector<vector<LL>> dp(n , vector<LL>(k + 1 , -inf));
//dp[i][j] : 对于前 i 个数,已经删除了 j 个,并且一定保留第 i 个数的最大值。
//这也就是为什么要引入 C[N + 1] 因为一定保留 i , 就要去枚举 i。
//但所有的 i 都能接上 C[N + 1] (因为 C[N + 1] 的颜色与所有的 c[i] 均不同)。
dp[0][0] = 0;
for (int i = 1 ; i <= n - 1 - k ; ++i) {
pair<LL,int> mx[2] = {{-inf , 0} , {-inf , 0}};
for (int j = i ; j <= i + k ; ++j) {
pair<LL,int> pre = {dp[j - 1][j - i] , c[j - 1]};
if (pre > mx[0]) {
swap(pre , mx[0]);
}
if (mx[0].second == mx[1].second || (pre.first > mx[1].first && pre.second != mx[0].second)) {
mx[1] = pre;
}
dp[j][j - i] = mx[c[j] == mx[0].second].first + v[j];
//注意 dp[i][i - s] 并未加入 mx 更新
}
}
cout << max(-1LL , dp[n - 1][k]) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}