洛谷P3620 [APIO/CTSC 2007]数据备份——题解

题目传送门


思考过程&具体做法:
显然,我们选作答案的每一个点对中的点都是相邻的,我们将这些数两两作差,于是问题变成了在n-1个数中选出k个不相邻的数,和最小为多少。
首先考虑初步贪心,即每次都取最小的数,但显然这样是错误的。
我们要思考怎么贪心才是对的。
我们发现,可以在取完最小的数后,删掉他的前一个数和后一个数,插入前一个数和后一个数的和减去当前数的差,这样如果取了插入的数,相当于没有取
当前最小的数,而是取了左右的两个数。我们找到了正确的贪心策略。
这样每次都相当于确定了一个数成为答案,将这个过程进行k次即可。
具体实现的话,因为一个数的前驱和后继在不断改变,我们需要用链表来维护,然后用堆来维护最小值。


代码:

#include 
using namespace std;

const long long inf=1e15,maxn=1e5+1000;
struct data
{
    long long id,v;
    friend bool operator < (data t1,data t2)
    {
        return t1.v>t2.v;
    }
};
priority_queue  q;
long long pre[maxn],nxt[maxn],a[maxn],val[maxn],cho[maxn];
long long ans,n,k;

int main()
{
    scanf("%lld%lld",&n,&k);
    for(long long i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(long long i=1;i1]-a[i];
        pre[i]=i-1,nxt[i]=i+1;
        data t; t.v=val[i],t.id=i;
        q.push(t);
    }
    val[0]=val[n]=inf;
    while(k--)
    {
        data now;
        while(1)
        {
            now=q.top();q.pop();
            if(!cho[now.id]) break;
        }
        long long id=now.id;
        ans+=val[id];
        long long pr=pre[id],nx=nxt[id];        
        val[id]=val[pr]+val[nx]-val[id];
        cho[pr]=cho[nx]=1;
        nxt[pre[pr]] = id; pre[id] = pre[pr];
        nxt[id] = nxt[nx]; pre[nxt[nx]] = id;
        pre[pr] = nxt[pr] = pre[nx] = nxt[nx] = 0;
        data t;t.id=id;t.v=val[id];
        q.push(t);
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(题解)