[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[i−1],f[j−1]+sum[i]−sum[j−1])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[j−1]+sum[i]−sum[j−1] ,发现 s u m [ i ] sum[i] sum[i]是定值,也就是说我们要求的其实是 f [ j − 1 ] − s u m [ j − 1 ] f[j-1]-sum[j-1] f[j−1]−sum[j−1]的最大值,我们利用 t m p tmp tmp数组来记录 f [ j − 1 ] − s u m [ j − 1 ] f[j-1]-sum[j-1] f[j−1]−sum[j−1]的最大值,那怎么解决同色的问题呢 ( 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[j−1]−sum[j−1]的最大值,这样我们就可以把第二个循环砍掉,变成复杂度是 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;
}