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;
}