BZOJ 2151 种树 贪心+优先队列+HASH

来源:互联网 发布:淘宝无法付款怎么回事 编辑:程序博客网 时间:2024/06/13 21:15

题意:链接

方法:贪心+优先队列

解析:

首先裸上贪最大的肯定不对。

DP可行么?可行,两个数组一个记录选一个记录不选好像差不多n^2?

不过还是想想贪心。

贪最大的为什么不对?

因为有可能它旁边的两个加起来比它更优越?

所以能否找到一点关系呢?

既然话都说到这了,两个加起来可能更优越。

所以我们在选了最大的之后是否应该回推一个值呢?代表选旁边俩。

我们发现选旁边俩是两个单位,选一个是一个单位,如果我们推回去某个值后这个值相当于对应删去一个单位选两个单位,即增加一个单位,这显然不会影响最终我们选几个单位。

那么这个值是啥呢?

a[i+1]+a[i-1]-a[i]

很显然吧,所以我们只需要每次删掉三个东西推进去一个即可。

不影响我们选的东西的数量。

那么复杂度呢?

我们可以搞一个优先队列,然后hash标记某个点是否已删。

然后开两个数组记录某个值前面的点以及后面的点。

代码:

#include <queue> #include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 200010using namespace std;int n,m;int a[N];int be[N];int af[N];int hash[N];struct node{    int no;    int val;}tmp[N];int tot;priority_queue <node>q;bool operator < (node s,node t){    return s.val<t.val;}int main(){     scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        if(i==1)be[i]=n;        else be[i]=i-1;        if(i==n)af[i]=1;        else af[i]=i+1;        scanf("%d",&a[i]);        node tmp;        tmp.no=i,tmp.val=a[i];        q.push(tmp);    }    if(m>(n/2)){puts("Error!");return 0;}    int ans=0;    for(int i=1;i<=m;i++)    {        node u=q.top();        q.pop();        while(hash[u.no])        {            u=q.top();            q.pop();        }        ans+=u.val;        int bex=be[u.no],afx=af[u.no];        hash[bex]=hash[afx]=1;        tot=0;        u.val=a[u.no]=a[bex]+a[afx]-a[u.no];        af[u.no]=af[afx],be[u.no]=be[bex];        be[af[afx]]=u.no,af[be[bex]]=u.no;        q.push(u);    }    printf("%d\n",ans);} 
0 0
原创粉丝点击