题意
给出n个点的位置,选择不重复的2*k个点进行匹配,求匹配后两点距离最小和。
题解
堆+链表+贪心
先进行一步转换,相邻间求个距离。现在问题就转化成了在n个数中选取互不相邻的k个数,使得总和最小。
当k=1时,选取最小值。
当k=2时,要么选最小值(位置i)和不相邻(即除i,i-1,i+1外)的次小值,要么选i-1和i+1。
基于这两点我们就可以开始设置后悔机制了。当选择了a[i]后,我们a[i]赋予a[i-1]+a[i+1]-a[i]的新值,删掉a[i-1]和a[i+1]。
在选了i之后,如果想改成i-1和i+1,就再选一次a[i]这样ans=a[i] + a[i-1]+a[i+1]-a[i]=a[i-1]+a[i+1],成功完成转换,并且用了两次的操作代价,与题意相符。
因为要和最小,我们把所有有意义的a[i]放入小根堆,每次取出堆顶进行如上操作。
重复k次即可解决此题。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=(1<<30)-1;
const int maxn=1e5+10;
int n,k;
int a[maxn];
struct Q
{
int a,p;
bool operator<(Q q1) const
{
return a>q1.a;
}
};priority_queue<Q> q;bool v[maxn];
struct N
{
int prev,next,val;
}node[maxn];int len=0;
void insert(int val)
{
node[++len]=(N){len-1,0,val};
node[len-1].next=len;
}
void remove(int p)
{
node[node[p].prev].next=node[p].next;
node[node[p].next].prev=node[p].prev;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
a[i]=a[i+1]-a[i];
q.push((Q){a[i],i});
insert(a[i]);
}
node[0].val=node[++len].val=inf;
int ans=0;
for(int i=1;i<=k;i++)
{
Q tp;
while(tp=q.top(),q.pop(),v[tp.p]);
ans+=node[tp.p].val;
int prev=node[tp.p].prev,next=node[tp.p].next;
tp.a=node[tp.p].val=node[prev].val+node[next].val-node[tp.p].val;
remove(prev);v[prev]=true;
remove(next);v[next]=true;
q.push(tp);
}
printf("%d\n",ans);
return 0;
}