为什么每次考试的原题我都没做过呀, $QAQ$
solution
当我们看到这道题时,不难想到这是一道 $dp$ 题。 开个玩笑, $2e5$ 的数据范围,怎么可能 $n ^ 2 Dp$ 。
回到正题:一道经典的带悔贪心。
我们先模拟一下一般的贪心策略,每次选取最大的可取的值,直到不能取为止。
不难发现,这个策略是错的。我们来看一下这个图:
对于第一次策略,我们会优先选择最大的20号点,紧接着,我们就只能憋屈的选择1号点,很明显正解不可能是21, $19+19 = 38$ 明显更优,那么我们应该怎样调整我们的贪心策略才能保证我们贪心的正确性呢,其实并不是很难,我们重新看一幅图。
对于这次贪心策略,我们取走20以后顺手删除(左)19点和(右)19点,并将20号点更新为: $19 + 19 - 20 = 18$ ,即:
此时,我们再取走18号点,并删除与18号点相邻的1号点,并将 $-17$ 号点加入队列中:
根据题目的意思,现在我们无论取多少都无所谓,所以放弃-17,得到答案38正解,这样不但避免了选择相邻的两个(选一个的时候,另外两个被删除了),还保证了价值守恒(请自行理解),那么这道题就完结撒花啦。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include<bits/stdc++.h> #define LL long long using namespace std; const int N = 5e5 + 5; struct node{ int l, r; LL val; }a[N]; struct Node{ int id; LL val; bool operator <(Node b)const { return val < b.val; } }; int n, m; LL ans; bool vis[N]; priority_queue<Node>q; void Del(int x) { a[x].l = a[a[x].l].l; a[x].r = a[a[x].r].r; a[a[x].l].r = x; a[a[x].r].l = x; } int main() { scanf("%d%d", &n, &m); for(int i = 1;i <= n;i ++) { scanf("%lld", &a[i].val); a[i].l = i - 1; a[i].r = i + 1; q.push((Node){i, a[i].val}); } for(int i = 1;i <= m;i ++) { while(vis[q.top().id]) q.pop(); Node now = q.top(); q.pop(); if(now.val < 0) break; ans += now.val; vis[a[now.id].l] = vis[a[now.id].r] = 1; a[now.id].val = a[a[now.id].l].val + a[a[now.id].r].val - a[now.id].val; q.push((Node){now.id, a[now.id].val}); Del(now.id); } printf("%lld", ans); return 0; }
|