BZOJ 2288: 【POJ Challenge】生日礼物 优先队列+贪心+链表

这题看别人题解的

这题说可以转换成数据备份。
这题可以把一段同号的数并成一个数,那么就变成了一个正负交替的序列,然后把头尾的负数去掉。
然后就是把所有的正值都加起来,并统计正数的段数cnt,如cnt<=m,那么这些整数的和就是答案

如果cnt>m,那我们就要不断地去掉一段,直到cnt=m

先把所有数的绝对值加进优先队列,每次去一个最小的出来然后减掉,最后的即为答案。
为什么是正确的呢?因为如果减去的值在原数列中为正则相当于不要这个数,否则就相当于选了这个负数然后把两边的正值合并起来。

无论减去正数还是负数,都要保证每次操作能够使段数cnt-1,这是我们的目的所在

既如果去掉的是正数,保证了cnt-1,如果去掉了负数,那么要保证它的两边必须要是正数,这样才能使旁边两个正数加上这个负数,使得我们原来取的它的旁边的两段正数加上这个负数后,段数减一,所以处于左右端点的负数都要先去掉
但有一个问题,就是若选了一个数那么其两边的数就必定不能被选,那么就转换成了数据备份了。

 

真的是被这题搞得快自闭了,自己写的一直wa,经过不断修改代码,两个代码来回交,大概交了40次,才发现我哪里错了

错的
l[pos] = l[l[pos]]; r[pos] = r[r[pos]]; r[l[l[pos]]] = pos; l[r[r[pos]]] = pos;

这个是导致我WA了20次的原因,怎么也没想到是这个地方错了,我还在疯狂修改其他地方。

对的
r[l[l[pos]]] = pos;
l[r[r[pos]]] = pos;
l[pos] = l[l[pos]];
r[pos] = r[r[pos]];

原来错的那个我的 l[pos] 已经先更改过了。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e5 + 5;
int a[maxn], l[maxn], r[maxn],n,m,ans,cnt;
bool vis[maxn];
struct data
{
    int val, pos;
    data(int val1 = 0, int pos1 = 0) { val = val1; pos = pos1; }
    bool operator < (const data &a) const
    {
        return a.val<val;
    }
};
priority_queue<data> que;
int main()
{
    cin >> n >> m;
    int tot = 1;
    for (int i = 1; i <= n; i++)
    {
        int tmp;
        cin >> tmp;
        if (a[tot] * tmp >= 0) a[tot] += tmp;
        else a[++tot] = tmp;
    }
    int left = 1;
    if (a[1]<0) left++;
    if (a[tot]<0) tot--;
    for (int i = left; i <= tot; i++)
    {
        if (a[i] > 0) cnt++, ans += a[i];
        a[i] = abs(a[i]);
        que.push(data(a[i],i));
        //que.push(data(a[i],i));
        l[i] = i - 1;
        r[i] = i + 1;
    }
    l[left]=r[tot] = 0;
    if (cnt <= m) cout << ans << endl;
    else
    {
        int num = cnt - m;
        for (int i = 1; i <= num;i++)
        {
            while (vis[que.top().pos] && !que.empty())  que.pop();
            int pos = que.top().pos;
            que.pop();
            ans -= a[pos];
            if (!l[pos]) 
            {
                vis[pos] = vis[r[pos]] = true;
                l[r[r[pos]]] = 0;
            }
            else if (!r[pos])
            {
                vis[pos] = vis[l[pos]]=true;
                r[l[l[pos]]] = 0;
            }
            else
            {
                vis[l[pos]] = vis[r[pos]] = true;
                a[pos] = a[l[pos]] + a[r[pos]] - a[pos];
                que.push(data(a[pos],pos));
                r[l[l[pos]]] = pos;
                l[r[r[pos]]] = pos;
                l[pos] = l[l[pos]];
                r[pos] = r[r[pos]];

            }
        }
        cout << ans << endl;

    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/xiaoguapi/p/10371599.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值