思路太难想了……
不难想到转移方程:
\[ f[i][j]=max\{ f[i][j],f[i-1][j],f[i-2][j-1]+a[i] \}; \]
再看了一眼数据范围……emmmmmm……糊锅了……
(25行代码捞40分算了)
正确做法是用双向链表+优先队列
将这些价值用链表存储,同时放进优先队列里面维护,每次取价值最大的坑种树。但是这样贪心可能是不优的,因为考虑\(1,8,9,8,1\)的情况发现直接贪心是不行的。
考虑贪心不优的情况。我们取了一个眼前最大的值\(u\),害的这个坑左右两边的坑\(l,r\)都不可以选。站在上帝视角,我们只有选了这两个坑\(l,r\)并且眼前这个最大的坑\(u\)不选才能达到最优。为了弥补两个坑\(l,r\)带来的损失,我们在双向链表中删掉\(l,r\)但是不删除\(u\),我们要利用\(u\)节点让后来我们可以“反悔”我们开始那个错误的操作。保留\(u\)节点在双向链表中的位置,而\(u\)的价值改成\(l.val+r.val-u.val\),再丢回优先队列里面。下次取优先队列\(Q\)的头的时候就会有机会选中这个点,这样我们的累加值加上这个点的价值\(l.val+r.val-u.val\),总体上看累加值增加了\(l.val+r.val\),就好像我们一开始选了两边的坑\(l,r\)没选\(u\)一样。并且因为双向链表的特性,我们如果选了那个价值为\(l.val+r.val-u.val\)的“反悔”过的u,那现在的u的左右两边的坑,也就是之前\(l\)的左边坑和\(r\)的右边坑的都不可以选了;如果现在反悔过的\(u\)值比当前的左或者右节点都小那我们就不反悔,因为优先队列中总是大的价值在前面,选了当前左或者右的价值更大的坑之后,那个“可供反悔”的\(u\)就会被删除。
这样看来,坑的可选性是一致的。
因此我们的算法就是:每次取优先队列的头的价值进行累加,\(k\)次之后得到答案。
注意一些细节问题,比如说打死都不选负的价值的点,什么删了头尾指针这种操作就不要打出来之类的……
那代码就不难写出了。
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x = 0;
char c = getchar();
bool minus = 0;
for (; !isdigit(c); c = getchar())
if (c == '-')
minus = 1;
for (; isdigit(c); c = getchar())
x = x * 10 + c - '0';
if (minus)
return -x;
else
return x;
}
const int N = 1e6 + 5,INF=0x3f3f3f3f3f3f3f3f;
struct node
{
int val, ind;
node(int value, int index)
{
val = value;
ind = index;
}
bool operator<(const node &a) const
{
return val < a.val;
}
};
struct dq
{
int val, pre, next;
} p[N];
int n, k, ban[N];
priority_queue<node> Q;
void dele(int i)
{
p[p[i].pre].next = p[i].next;
p[p[i].next].pre = p[i].pre;
}
signed main()
{
cin >> n >> k;
p[0].next = 1;
p[n + 1].pre = n;
for (int i = 1; i <= n; i++)
{
int v = read();
Q.push(node(v, i));
p[i].val = v;
p[i].pre = i - 1;
p[i].next = i + 1;
}
int ans =0;
while (k--&&!Q.empty())
{
node u = Q.top();
Q.pop();
if (ban[u.ind])
{
k++;
continue;
}
if(u.val<=0)
{
dele(u.ind);
k++;
continue;
}
ans += p[u.ind].val;
ban[p[u.ind].pre] = ban[p[u.ind].next] = 1;
p[u.ind].val = p[p[u.ind].pre].val + p[p[u.ind].next].val - p[u.ind].val;
if (p[u.ind].pre != 0) dele(p[u.ind].pre);
if (p[u.ind].next != n + 1) dele(p[u.ind].next);
Q.push(node(p[u.ind].val, u.ind));
}
cout<<ans;
return 0;
}