题意
有n栋楼排成一排,要求在k对楼之间连接电缆,每栋楼只能被连接一次,求最小的
∑|dis[x]−dis[y]|
.x,y分别为电缆的两个端点。
n<=100000
分析
显然k对电缆连接的必然都是相邻的两栋楼,那么问题可以转换成有n-1个数,a[i]=dis[i+1]-dis[i],从中取出k个数满足其互不相邻且和最小。
那么就可以用贪心来做。每次找一个最小的,假设是x,那么x+1和x-1要么不选,要么都选,那么可以把这三个数合并成一个数,权值为a[x+1]+a[x-1]-a[x],然后把原来的三个数删掉即可。
可以用优先队列和双向链表来维护。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 100005
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct data
{
int pos;
ll val;
bool operator < (const data &a) const
{
return val<a.val;
}
};
priority_queue <data> q;
int n,m,next[N],last[N],vis[N];
ll a[N],dis[N];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&dis[i]);
for (int i=1;i<=n;i++)
{
if (i<n) a[i]=dis[i]-dis[i+1];
else a[i]=-inf;
data u;
u.val=a[i];u.pos=i;
q.push(u);
next[i]=i+1;last[i]=i-1;
}
next[n]=1;last[1]=n;
int ans=0;
for (int i=1;i<=m;i++)
{
data u=q.top();
q.pop();
while (vis[u.pos]&&!q.empty())
{
u=q.top();
q.pop();
}
ans-=u.val;
int x=u.pos;
data v;
v.pos=x;v.val=a[x]=a[last[x]]+a[next[x]]-a[x];
q.push(v);
next[last[last[x]]]=x;
last[next[next[x]]]=x;
vis[next[x]]=1;vis[last[x]]=1;
next[x]=next[next[x]];last[x]=last[last[x]];
}
printf("%d",ans);
return 0;
}