bzoj1150 [CTSC2007]数据备份Backup(贪心+堆)

这题好神啊orz
我们首先肯定是取相邻的两个点连边。
于是我们可以先处理出每相邻两点的距离,然后由于不能选同一个点,我们就当于是在n-1个数中选k个,彼此不能相邻,使得和最小。
我们贪心地取最小,每次取了a[x],删掉左边的右边的和a[x],然后加进去一个a[l]+a[r]-a[x],表示反悔了,不取a[x],取a[l]和a[r]。用一个堆来维护即可。
注意边界的情况:如果你取了最左端的数,那么次左端的数一定不会被选。贪心地证明一下即可。
因此你可以在最左端和最右端都加一个inf。
这样取K次即可。

#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,K,a[N],pre[N],nxt[N];
bool del[N];ll ans=0;
struct cmp{
    bool operator()(int x,int y){return a[x]>a[y];}
};
priority_queue<int,vector<int>,cmp>q;
int main(){
//  freopen("a.in","r",stdin);
    n=read();K=read();a[1]=read();
    for(int i=2;i<=n;++i) a[i]=read(),a[i-1]=a[i]-a[i-1];
    --n;a[0]=inf;
    for(int i=1;i<=n;++i) q.push(i),pre[i]=i-1,nxt[i]=i+1>n?0:i+1;
    while(K--){
        int x=q.top();q.pop();while(del[x]){x=q.top();q.pop();}
        ans+=a[x];del[pre[x]]=1;del[nxt[x]]=1;a[x]=a[pre[x]]+a[nxt[x]]-a[x];q.push(x);
        pre[x]=pre[pre[x]];nxt[x]=nxt[nxt[x]];nxt[pre[x]]=x;pre[nxt[x]]=x;
    }printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(STL,bzoj,贪心)