【题意】
一条街上有n栋楼,你需要为k对楼连上电缆(2*k<=n),每对代价为两栋楼之间的距离。
每栋楼只能被配对一次,求配对最小代价。
【思路】
十分巧妙。
很明显一开始计算出每相邻两栋楼的距离差,然后从中选k个。
首先想到的应当是贪心,从小到大选,可问题在于不能选相邻两个。
其根本在于,我们选小的x,有可能同时选左边的l与右边的r可能获得更优解,所以不能使用贪心。
那么我们只需要给后面留一个反悔的机会,不选x而是选l与r就好了!
具体的方法是,设val[x]为x具体的值,选择x以后,将其从小顶堆中弹出,删除l与r,再加入一个值为val[l]+val[r]-val[x]的元素,如果这个值比较小被后面贪心选中,就相当于反悔不选x而选l与r了。
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define MAXN 110000
#define pa pair<int,int>
#define mp make_pair
#define inf 0x3f3f3f3f
using namespace std;
priority_queue< pa,vector<pa>,greater<pa> > list;
int n,m,a[MAXN],pre[MAXN],nex[MAXN];
int read()
{
int ans=0,f=1,x;
while(1)
{
x=getchar();
if(x=='-')f=-1;
if(x>='0'&&x<='9')break;
}
while(x>='0'&&x<='9')
{
ans=ans*10+x-'0';
x=getchar();
}
return ans*f;
}
int main()
{
n=read();m=read();
int last=0;
for(int i=1;i<=n;i++)
{
int x=read();
a[i]=x-last;
pre[i]=i-1;
nex[i]=i+1;
last=x;
}
pre[2]=0;
nex[n]=0;
for(int i=2;i<=n;i++)
list.push(mp(a[i],i));
int ans=0;
for(int i=1;i<=m;i++)
{
while(list.top().first!=a[list.top().second]) list.pop();
int val=list.top().first,x=list.top().second,l=pre[x],ll=pre[l],r=nex[x],rr=nex[r];
ans+=val;
list.pop();
nex[ll]=x;
pre[x]=ll;
nex[x]=rr;
pre[rr]=x;
if(l&&r)a[x]=a[l]+a[r]-a[x];
else a[x]=inf;
a[l]=a[r]=inf;
list.push(make_pair(a[x],x));
}
printf("%d\n",ans);
return 0;
}