LGOJ P1484 种树

P1484 种树

思路太难想了……

不难想到转移方程:
\[ 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;
}

猜你喜欢

转载自www.cnblogs.com/kion/p/11861784.html