jzoj4726. 【NOIP2016提高A组模拟8.22】种花 贪心+堆

版权声明:转载注意打标记哦( •̀ ω •́ )y https://blog.csdn.net/qq_35866453/article/details/76974546

这题思路很巧妙,要借鉴。
题意:给你一个环,选出M个点,任意两个互不相邻,要求总和最大。
一开始想的dp,设f[i][0/1]表示i选不选的最大总和,然后发现i的取值基本上能和他前面所有的值都有关联,由于是个环,所以dp你连预处理都会炸。。如果是个序列就可以这么做,不过在后面复制一遍然后用单调队列优化好像也可以,但那就太不优美了其实是我懒得打
那么我们可以贪心,把所有的数加入大根堆,每次取出最上面的,但是这样肯定不是最优秀的,我们可以手动处理出一种撤销操作,这是本题的核心所在:
取出一个数累加到答案以后,把他相邻的两个删除掉,把取出的这个值变为左右之和与这个值的差,但是这个数是可以重新选的。这个操作什么意思呢?
就是说,删除掉旁边两个说明旁边两个不能选,如果有一次选中了,说明这个数没改变之前的值不是最优的,那么就是选旁边两个,此时恰好选了两次,又把原来的数给减掉了,不得不说非常的妙。
要提前预处理一个链表,无解的情况随便判断一下就好了。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=3e5+5;
typedef long long ll;
bool vis[N];
int pre[N],next[N];
struct node
{
    int x;
    node(int y){x=y;}
    node() {}

};
priority_queue<node> q;
ll ans;
int a[N],n,m;
bool operator < (node x,node y)
{
    return a[x.x]<a[y.x];
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&a[i]);
    fo(i,1,n)
    {
        pre[i]=i==1?n:i-1;
        next[i]=i==n?1:i+1;
        q.push(i);
    }
    if (m>n/2)printf("Error!\n");
    else
    {
        fo(i,1,m)
        {
            node now=q.top();
            while (vis[now.x])
            {
                q.pop();
                now=q.top(); 
            }   
            int x=now.x;
            q.pop();
            ans+=a[x];
            a[x]=a[pre[x]]+a[next[x]]-a[x];
            vis[pre[x]]=vis[next[x]]=1;
            next[x]=next[next[x]];
            pre[next[x]]=x;
            pre[x]=pre[pre[x]];
            next[pre[x]]=x; 
            q.push(node(x));
        }
        printf("%lld\n",ans);
    }
}
展开阅读全文

没有更多推荐了,返回首页