BZOJ 1150: [CTSC2007]数据备份Backup【贪心】

1150: [CTSC2007]数据备份Backup

【题目描述】
传送门

【题解】

都说贪心是最难的,果然很难。
我们肯定选择相邻的连边,这题就变成了从n-1条边中选出k条不相邻的边的最小代价。
这题我们有个贪心的想法,挑小的好,但是肯定有反例,那么我们取出一条边时,将这条边两边的边放进去,新的代价是放入的两条边-取出的边,下一次取到这个也就表示用旁边两条边替换中间这条边。然后用链表维护一下就可以了。

代码如下

#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define MAXN 100005
using namespace std;
typedef long long LL;
typedef pair<LL,int> Pair;
int n,K,len,a[MAXN];
LL W[MAXN],Ans;
Pair hep[MAXN];
bool vis[MAXN];
int lst[MAXN],nxt[MAXN];
void put(Pair x){
    x.first=-x.first;
    hep[++len]=x;
    push_heap(hep+1,hep+1+len);
}
Pair get(){
    pop_heap(hep+1,hep+1+len);
    Pair x=hep[len--];x.first=-x.first;
    return x;
}
int read(){
    int ret=0;char ch=getchar();bool f=1;
    for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
    for(; isdigit(ch);ch=getchar()) ret=(ret<<3)+(ret<<1)+ch-48;
    return f?ret:-ret;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("prob.in","r",stdin);
    freopen("prob.out","w",stdout);
    #endif
    n=read();K=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++) W[i]=a[i+1]-a[i];n--;
    for(int i=1;i<=n;i++) lst[i]=i-1,nxt[i]=i+1,put(make_pair(W[i],i));
    W[0]=W[n+1]=W[n+2]=1e17;lst[0]=nxt[n+1]=n+2;
    for(int i=1;i<=K;i++){
        Pair Now=get();while(vis[Now.second]) Now=get();
        int id=Now.second;LL x=Now.first;
        int L=lst[id],R=nxt[id];Ans+=x;
        vis[L]=vis[R]=1;lst[id]=lst[L],nxt[id]=nxt[R];
        nxt[lst[id]]=lst[nxt[id]]=id;W[id]=W[L]+W[R]-x;
        put(make_pair(W[id],id));
    }
    printf("%lld\n",Ans);
    return 0;
}
posted @ 2018-05-27 15:17  XSamsara  阅读(137)  评论(0编辑  收藏  举报