洛谷 P9688 Colo. 的题解

题意转化

原题

别看题目说的那么繁琐,又剪又拼的,其实概括一下就是一句话:

一个有 n n n 个格子的长纸条,第 i i i 个格子颜色为 a i a_i ai,第 i i i 种颜色价值 b i b_i bi,现在要任选 k k k 种颜色,使得 所有颜色是较大编号 的格子都在 较小编号 的后面,求最大价值。

大体思路

阅读题目可以发现 O ( n 3 ) O(n^3) O(n3) 是可以过的(洛谷评测机 1 0 9 10^9 109 开 O2 都能过, 1 0 8 10^8 108 显然是可以的),那么我们可以想到使用 dp。不像某些题解的代码那么长,其实只需要处理一下题目中额外的要求就行了。

d p i , j dp_{i,j} dpi,j 表示考虑前 i i i 种颜色并选用第 i i i 种颜色,一共选择了 j j j 种颜色的价值。

转移方程为 d p i , j = max ⁡ ( d p i , j , d p m , j − 1 ) + b a i dp_{i,j}= \max(dp_{i,j}, dp_{m,j-1}) + b_{a_i} dpi,j=max(dpi,j,dpm,j1)+bai m m m 是在前面选中的颜色下标(即 0 ≤ m < i 0 \leq m < i 0m<i)。

我们还要定义两个数组 l , r l,r l,r 分别表示一种颜色第一次出现的位置和最后一次出现的位置,便于在 dp 中判断。这个题目就做完了。

代码如下:

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

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
	return x * f;
}

int n, k; ll ans = -1;
int a[507], b[507];
int l[507], r[507]; //这两个数组记录颜色为 i 的区间
ll dp[507][507];
//dp[i][j] 表示考虑前 i 种颜色并选用第 i 种颜色,一共选择了 j 种颜色的方案数

int main() {
	n = read(), k = read();
	for(int i = 1; i <= n; i++) {
		a[i] = read();
		if(l[a[i]] == 0) l[a[i]] = i;
		r[a[i]] = i;
	}
	for(int i = 1; i <= n; i++) b[i] = read();
	memset(dp, -0x3f, sizeof(dp)); dp[0][0] = 0;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= k; j++)
			for(int m = 0; m < i; m++)
				if(a[i] > a[m] && l[a[i]] > r[a[m]]) //如果满足“单调不下降”这一要求
					if(dp[m][j - 1] >= 0)
						dp[i][j] = max(dp[i][j], dp[m][j - 1] + b[a[i]]);
	for(int i = 1; i <= n; i++)
		ans = max(ans, dp[i][k]);
	printf("%lld", ans);
	return 0;
}
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值