一、题目链接
二、题目大意
在一条数轴上有 n n n个点,每相邻两点之间构成一个距离,选择互不相邻的 K K K个距离,使得这 K K K个距离之和最小。
数据范围: n ≤ 100000 n \leq 100000 n≤100000, 1 ≤ k ≤ n 2 1 \leq k \leq \frac{n}{2} 1≤k≤2n。
三、题目分析
-
因为要找 k k k个的和最小,如果不考虑题目中互不相邻的条件,我们可以直接贪心的寻找前 i i i个最小的距离。
-
而此时要求互不相邻的情况,我们假设此时剩下的距离中最小的是 X [ i ] X[i] X[i],如果选择 X [ i ] X[i] X[i],势必会影响 X [ i − 1 ] X[i-1] X[i−1], X [ i + 1 ] X[i+1] X[i+1]的选择情况,所以我们将左右两段距离 X [ i − 1 ] X[i-1] X[i−1], X [ i + 1 ] X[i+1] X[i+1]同时进行考虑。
-
因为 K = 1 K=1 K=1直接选择 X [ i ] X[i] X[i] 即可, K > 2 K>2 K>2时的情况可以由 K = 2 K=2 K=2同理得到。此时考虑 K = 2 K=2 K=2有几种情况:
- 如果我们选择了 X [ i ] X[i] X[i],那么 X [ i − 1 ] X[i-1] X[i−1]和 X [ i + 1 ] X[i+1] X[i+1]都不能选择了,将选择除这三个以外的最小值。答案为 X [ i ] + M i n X[i]+Min X[i]+Min。
- 如果我们选择了 X [ i − 1 ] X[i-1] X[i−1]和 X [ i + 1 ] X[i+1] X[i+1],那么就不能选择 X [ i ] X[i] X[i]。答案为 X [ i − 1 ] + X [ i + 1 ] X[i-1]+X[i+1] X[i−1]+X[i+1]
- 如果我们只选择 X [ i − 1 ] X[i-1] X[i−1]和 X [ i + 1 ] X[i+1] X[i+1]中的一个,再选择除这三个以外的最小值,那么答案一定大于 X [ i ] + M i n X[i]+Min X[i]+Min。因为 X [ i − 1 ] X[i-1] X[i−1]和X[i+1]都大于X[i]。
-
所以最后我们发现,我们只有两种答案:
(1) X [ i ] + M i n X[i]+Min X[i]+Min(2) X [ i − 1 ] + X [ i + 1 ] X[i-1]+X[i+1] X[i−1]+X[i+1]
那么我们如何判断这两个答案的大小呢?因为最小值显然会根据取数的不同发生变化,所以我们需要一个二叉堆来维护最小值(优先队列也可)。
-
我们发现,只有 M i n < X [ i − 1 ] + X [ i + 1 ] − X [ i ] Min<X[i-1]+X[i+1]-X[i] Min<X[i−1]+X[i+1]−X[i]的时候,答案(1)最小。还有一个巧合的地方在于, X [ i ] + X [ i − 1 ] + X [ i + 1 ] − X [ i ] = X [ i − 1 ] + X [ i + 1 ] X[i]+X[i-1]+X[i+1]-X[i]=X[i-1]+X[i+1] X[i]+X[i−1]+X[i+1]−X[i]=X[i−1]+X[i+1]即答案(2)。
-
因为已经有了二叉堆,所以我们直接取出 X [ i ] X[i] X[i],删除 X [ i ] X[i] X[i], X [ i − 1 ] X[i-1] X[i−1]和 X [ i + 1 ] X[i+1] X[i+1],再把 X [ i − 1 ] + X [ i + 1 ] − X [ i ] X[i-1]+X[i+1]-X[i] X[i−1]+X[i+1]−X[i]入堆,代替原来他们三个的位置成为一个新的距离即可。
- 若 M i n < X [ i − 1 ] + X [ i + 1 ] − X [ i ] Min<X[i-1]+X[i+1]-X[i] Min<X[i−1]+X[i+1]−X[i],则堆顶即为 M i n Min Min,答案为 X [ i ] + M i n X[i]+Min X[i]+Min
- 若 M i n > X [ i − 1 ] + X [ i + 1 ] − X [ i ] Min>X[i-1]+X[i+1]-X[i] Min>X[i−1]+X[i+1]−X[i],则堆顶为 X [ i − 1 ] + X [ i + 1 ] − X [ i ] X[i-1]+X[i+1]-X[i] X[i−1]+X[i+1]−X[i],答案为 X [ i − 1 ] + X [ i + 1 ] X[i-1]+X[i+1] X[i−1]+X[i+1]
四、题目细节:
- 因为要实现6中的操作,所以我们需要一个堆,以及一个链表。
- 为了保证能够删掉堆和链表中的正确元素,我们需要两个指针数组来从堆指向链表和从链表指向堆。
- 因为首尾两个距离没有 i − 1 i-1 i−1或 i + 1 i+1 i+1,所以我们人为添加一个正无穷在一头一尾表示访问越界。
五、正解程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long ll;
const ll maxn=100010;
struct node
{
ll val;
ll pos;//指针1
ll prev,next;
}D[3*maxn];
struct ano//手动堆的实现
{
ll val;
ll pos;//指针2
}heap[3*maxn];
ll n,K,count1=0,head,tail,tot=0;
ll num[maxn];
void init()
{
tot=2;
head=1;tail=2;
D[head].next=tail;
D[head].val=0x7fffffff;//设置边界
D[tail].val=0x7fffffff;//设置边界
D[tail].prev=head;
}
void remove(ll p)
{
D[D[p].prev].next=D[p].next;
D[D[p].next].prev=D[p].prev;
}
ll insert(ll p,ll val)
{
ll q=++tot;
D[q].val=val;
D[D[p].next].prev=q;
D[q].next=D[p].next;
D[p].next=q;
D[q].prev=p;
return q;
}
void up(ll p)
{
while(p>1)
{
if(heap[p].val<heap[p/2].val)
{
swap(D[heap[p].pos].pos,D[heap[p/2].pos].pos);
//堆更新,链表指向堆的指针也要更新
swap(heap[p],heap[p/2]);
p>>=1;
}
else
break;
}
}
void Insert(ano x)
{
heap[++count1]=x;
D[x.pos].pos=count1;
up(count1);
}
void down(ll p)
{
ll s=p*2;
while(s<=count1)
{
if(s<n && heap[s].val>heap[s+1].val)
s++;
if(heap[s].val<heap[p].val)
{
swap(D[heap[s].pos].pos,D[heap[p].pos].pos);
//堆更新,链表指向堆的指针也要更新
swap(heap[s],heap[p]);
p=s;
s=p*2;
}
else
break;
}
}
void Remove(ll p)
{
D[heap[count1].pos].pos=p;
heap[p]=heap[count1--];
up(p);down(p);
}
int main()
{
scanf("%lld%lld",&n,&K);
for(ll i=1;i<=n;i++)
scanf("%lld",&num[i]);
n--;
init();
ano temp;
temp.pos=1;temp.val=D[head].val;
Insert(temp);
temp.pos=2;temp.val=D[tail].val;
Insert(temp);
ll lastone=1;
for(ll i=1;i<=n;i++)
{
ano temp;
temp.pos=insert(lastone,num[i+1]-num[i]);
lastone=temp.pos;
temp.val=num[i+1]-num[i];
Insert(temp);
}
ll ans=0;
for(ll i=1;i<=K;i++)
{
ll f1=D[heap[1].pos].prev,f2=D[heap[1].pos].next;
ll t1=D[f1].val,t2=D[f2].val,tp=heap[1].val,used=heap[1].pos;
ll flag=D[f1].prev;
ans+=tp;
//删除三个数
remove(used);remove(f1);remove(f2);
Remove(1);Remove(D[f1].pos);Remove(D[f2].pos);
ano temp;
temp.pos=insert(flag,t1+t2-tp);
temp.val=t1+t2-tp;
Insert(temp);
}
printf("%lld",ans);
return 0;
}