[LibreOJ - 507] 接竹竿 (前缀和优化dp)

[LibreOJ - 507] 接竹竿


题目链接


大致题意:

一共有n张牌,每张牌上有一个花色c和一个点数v,花色不超过k 种。将这些牌依次放入一列牌的末端。若放入之前这列牌中已有与这张牌花色相同的牌,你可以选择将这张牌和任意一张花色相同的牌之间的所有牌全部取出队列(包括这两张牌本身),并得到与取出的所有牌点数和相同的分数。现在已知 这n张牌放入队列的顺序,求最多能得多少分


解题思路:

状态表示: f [ i ] f[i] f[i]表示选择第i张牌,得到的最大分

转移方程: f [ i ] = m a x ( f [ i − 1 ] , f [ j − 1 ] + s u m [ i ] − s u m [ j − 1 ] ) c [ i ] = = c [ j ] f[i]=max(f[i-1],f[j-1]+sum[i]-sum[j-1]) c[i]==c[j] f[i]=max(f[i1],f[j1]+sum[i]sum[j1])c[i]==c[j]

可以发现,转移方程是一个 O ( n 2 ) O(n^2) O(n2)的复杂度

for (int i = 1; i <= n; ++i) {
	for (int j = 1; j < i; ++j) {
		if (c[i] == c[j]) {
			f[i] = max(f[i - 1], f[j - 1] + sum[i] - sum[j - 1]);
		}
	}
}

通过观察转移方程 f [ j − 1 ] + s u m [ i ] − s u m [ j − 1 ] f[j-1]+sum[i]-sum[j-1] f[j1]+sum[i]sum[j1] ,发现 s u m [ i ] sum[i] sum[i]是定值,也就是说我们要求的其实是 f [ j − 1 ] − s u m [ j − 1 ] f[j-1]-sum[j-1] f[j1]sum[j1]的最大值,我们利用 t m p tmp tmp数组来记录 f [ j − 1 ] − s u m [ j − 1 ] f[j-1]-sum[j-1] f[j1]sum[j1]的最大值,那怎么解决同色的问题呢 ( c [ i ] = = c [ j ] ) (c[i]==c[j]) (c[i]==c[j]),我们定义 t m p [ i ] tmp[i] tmp[i]表示第i中颜色的 f [ j − 1 ] − s u m [ j − 1 ] f[j-1]-sum[j-1] f[j1]sum[j1]的最大值,这样我们就可以把第二个循环砍掉,变成复杂度是 O ( n ) O(n) O(n)的做法

for (int i = 1; i <= n; ++i) {
	f[i] = max(f[i - 1], sum[i] + tmp[a[i]]);
	tmp[a[i]] = max(tmp[a[i]], f[i - 1] - sum[i - 1]);
}

AC代码:

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1e6 + 10;
const ll INF = 2e18;
int n, m;
int a[N];
ll sum[N], tmp[N];
ll f[N];
int main(void)
{
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)cin >> a[i];
	for (int i = 1; i <= n; ++i) {
		int x; cin >> x;
		sum[i] = sum[i - 1] + x;
	}
	for (int i = 1; i <= m; ++i)tmp[i] = -INF;
	for (int i = 1; i <= n; ++i) {
		f[i] = max(f[i - 1], sum[i] + tmp[a[i]]);
		tmp[a[i]] = max(tmp[a[i]], f[i - 1] - sum[i - 1]);
	}
	cout << f[n] << endl;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值