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\) 个球必定入选作为了条件,简化了暴力状态,应为答案肯定是某个球作为结尾。

  1. 我们发现 \(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]\).
  2. 那么同理,\(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;

}



posted @ 2024-03-27 18:01  xqy2003  阅读(16)  评论(0编辑  收藏  举报