P3620 [APIO/CTSC 2007] 数据备份 题解
作者:互联网
题目大意
数轴上有\(N\)个点,可以用\(K\)条线连接\(2K\)条边,使得连的边的总长度最小
问题求解
显然,连接相邻的两个是最好的,所以可以建立\(N-1\)个差分,相邻的差分不能选,题目也就被转化成了,\(N-1\)个数里面选\(K\)个数,相邻的数不能选,使得数的总和最大
我们很容易可以想到一个贪心,用一个堆每次取小的,讲左边和右边标记为不可选点,但是也很显然是错误的:\(2 1 2 6\)就可以\(hack\)掉
所以考虑怎么返回,我们在选一个点时,要在堆中加入一个数,数的大小为左边数的大小\(+\)右边数的大小\(-\)自身的大小,连续取这个数两次就相当于这个数边上的两个数了,贪心求解即可
代码实现
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=100005,INF=0x3f3f3f3f;
int N,K,Ans,T;
int vis[maxn];
struct place{
int val,l,r;
}p[maxn];
struct AS{
int val,id;
bool operator <(const AS B)const {return val>B.val;}
};
priority_queue<AS> q;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void del(int x){
p[x].l=p[p[x].l].l;p[x].r=p[p[x].r].r;
p[p[x].l].r=x;p[p[x].r].l=x;
}
int main(){
freopen("P3620.in","r",stdin);
freopen("P3620.out","w",stdout);
T=read();
while(T--){
memset(p,0,sizeof p);
while(!q.empty())q.pop();
N=read();K=read();int lst=read();
for(int i=1;i<N;i++){
int now=read();
p[i].val=now-lst;
lst=now;p[i].l=i-1;p[i].r=i+1;
q.push((AS){p[i].val,i});
}
p[0].val=p[N].val=INF;
for(int i=1;i<=K;i++){
while(vis[q.top().id])
q.pop();
AS now=q.top();q.pop();
Ans+=now.val;vis[p[now.id].l]=vis[p[now.id].r]=1;
p[now.id].val=p[p[now.id].l].val+p[p[now.id].r].val-p[now.id].val;
q.push((AS){p[now.id].val,now.id});
del(now.id);
}
printf("%d\n",Ans);
}
return 0;
}
标签:ch,int,题解,CTSC,数据备份,read,while,ret,P3620 来源: https://www.cnblogs.com/martian148/p/15185665.html