打扑克
Problem:F
Time Limit:1000ms
Memory Limit:65535K
Description
一天,明明在玩纸牌游戏。 游戏规则是:一共有 n 张牌,每张牌上有一个花色 c 和一个点数 v,花色不超过 k 种。将这些牌依次放入一列牌的末端。若放入之前这列牌中已有与这张牌花色相同的牌,你可以选择将这张牌和任意一张花色相同的牌之间的所有牌全部取出队列(包括这两张牌本身),并得到与取出的所有牌点数和相同的分数。现在已知 明明 把这 n 张牌放入队列的顺序,求他最多能得多少分。 输入顺序即为 明明 放入队列的顺序。即,ci表示第 i 张放入的牌的花色,vi表示第 i 张放入的牌的点数。
Input
第一行两个整数 n,k。(1<n,k<2e5) 第二行,n 个整数 c1,c2,...,cn表示花色,满足 1≤ci≤k 第三行,n 个整数 v1,v2,...,vn 表示点数。
Output
输出一行一个整数,表示最多能得到的分数。
Sample Input
7 3 1 2 1 2 3 2 3 1 2 1 2 3 2 3
Sample Output
13
Hint
样例解释 1 第 1 步,向队列加入 1。现在的队列:1 第 2 步,向队列加入 2。现在的队列:1,2 第 3 步,向队列加入 1。现在的队列:1,2,1 第 4 步,向队列加入 2,取出 2,1,2。现在的队列:1 第 5 步,向队列加入 3。现在的队列:1,3 第 6 步,向队列加入 2。现在的队列:1,3,2 第 7 步,向队列加入 3,取出 3,2,3。现在的队列:1
思路:
dp,我原本想存一下当前点以前的相同花色的点,但发现会超时,于是我想只存两个点,一个点是上一个相同花色的位置,第二个点是除上一个点外,和当前点dp值最大的点,然后每次更换新一下这两个点;
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
const int N = 1e6;
long long f[N], val[N];
int p[N][4];
int c[N];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &val[i]);
val[i] += val[i - 1];
}
f[1] = 0;
p[c[1]][1] = p[c[1]][2] = 1;
for (int i = 2; i <= n; i++)
{
f[i] = f[i - 1];
for (int j = 1; j <= 2; j++)
{
if (p[c[i]][j])
f[i] = max(f[i], f[p[c[i]][j] - 1] + val[i] - val[p[c[i]][j] - 1]);
}
if (p[c[i]][1] && p[c[i]][2])
{
long long w1 = f[p[c[i]][1] - 1] + val[i] - val[p[c[i]][1] - 1], w2 = f[p[c[i]][2] - 1] + val[i] - val[p[c[i]][2] - 1];
if (w1 < w2)
{
p[c[i]][1] = p[c[i]][2];
}
}
else
p[c[i]][1] = p[c[i]][2] = i;
p[c[i]][2] = i;
}
printf("%lld\n", f[n]);
return 0;
}