题目
输入样例:
5 2
1
3
4
6
12
输出样例:
4
题目分析
初步思路
这道题一看,第一反应就是贪心,因为要总距离最小,所以肯定要每一段都尽量的小
换句话说,因为
k
<
n
2
k < \frac{n}{2}
k<2n 所以每一个最优解中的线段的两个端点一定是相邻的城市
A:
每次选其中最小的一段会不会影响其余的段(因为一个楼只能做为端点一次),使得全局答案不能为最优呢?
Q:
肯定的,举个例子
第一次最小肯定选
S
2
S_2
S2 ,然后第二次就只能选
S
1
+
S
2
+
S
3
S_1+S_2+S_3
S1+S2+S3 所以总计
S
2
+
(
S
1
+
S
2
+
S
3
)
S_2 + (S_1 + S_2+S_3)
S2+(S1+S2+S3)
但是换一种方法呢?
第一次选
S
1
S1
S1 ,然后第二次就可以选
S
3
S_3
S3,总计
S
1
+
S
3
S_1 + S_3
S1+S3 明显更优。
那么问题就来了,这道题是不是不能贪心的求解了呢?
正确思路
肯定不是的,古人云:知错能改,善莫大焉,那么如果我们再选择的时候已经知道自己错了,我们用已知的更好的答案修改以前的答案,最后的得到的就是更好的答案
先分析一下,还是刚才那幅图
如果我们选择
S
2
S_2
S2 那么就不能选
S
1
,
S
3
S_1,S_3
S1,S3
如果我们选择
S
1
,
S
3
S_1,S_3
S1,S3 那么就不能选
S
2
S_2
S2
所以综上我们有两种方法,全局的所有方法中取最好的
K
K
K中即可
好了,改错的思路有了,方法也很重要
首先因为我们知道的是每个点的坐标,所以先要处理出每条选段的长度
设
S
e
g
[
i
]
=
P
[
i
+
1
]
−
P
[
i
]
(
一种
P
是输入的楼房坐标
)
设 Seg[i] = P[i + 1] - P[i] (一种P是输入的楼房坐标)
设Seg[i]=P[i+1]−P[i](一种P是输入的楼房坐标)
然后我们要给程序反悔的机会
如果先选择了
S
e
g
[
i
]
Seg[i]
Seg[i] 然后发现不好,因该选
S
e
g
[
i
−
1
]
+
S
e
g
[
i
+
1
]
Seg[i - 1] + Seg[i + 1]
Seg[i−1]+Seg[i+1]则么办呢?
显然,减去
S
e
g
[
i
]
Seg[i]
Seg[i] 再加上
S
e
g
[
i
−
1
]
+
S
e
g
[
i
+
1
]
Seg[i - 1] + Seg[i + 1]
Seg[i−1]+Seg[i+1]即可
所以我们再选择 S e g [ i ] Seg[i] Seg[i]的同时,把 S e g [ i − 1 ] + S e g [ i + 1 ] − S e g [ i ] Seg[i - 1] + Seg[i + 1] - Seg[i] Seg[i−1]+Seg[i+1]−Seg[i]也加入备选的答案中不就可以了吗?这个显然可以用堆维护
那么最终的思路也就有了:
首先处理
S
e
g
[
i
]
=
P
[
i
+
1
]
−
P
[
i
]
Seg[i] = P[i + 1] - P[i]
Seg[i]=P[i+1]−P[i]
然后依次把
S
e
g
[
i
]
Seg[i]
Seg[i] 放入答案
每次选取备选答案中最小的计入答案
最后删除
S
e
g
[
i
−
1
]
,
S
e
g
[
i
]
,
S
e
g
[
i
+
1
]
Seg[i - 1],Seg[i],Seg[i + 1]
Seg[i−1],Seg[i],Seg[i+1]三个点(因为暂时都不能选了)
把
S
e
g
[
i
−
1
]
+
S
e
g
[
i
+
1
]
−
S
e
g
[
i
]
Seg[i - 1] + Seg[i + 1] - Seg[i]
Seg[i−1]+Seg[i+1]−Seg[i]插入
i
i
i号位置(加入备选答案)
因为每次 i − 1 i - 1 i−1, i + 1 i + 1 i+1不一定是原数组中相邻的两个,所以我们可以用链表维护,然后开一个 d e l del del 数组标记被删除就好了
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5, Inf = 1e9;
int n, k, pre[N + 5], nxt[N + 5];
long long ans,pos[N + 5], seg[N + 5];
bool del[N + 5];
struct cmp {
bool operator()(int x,int y) {
return seg[x] > seg[y];
}
}; // 一个很牛逼的技巧
priority_queue<int,vector<int>,cmp> q;
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%d", &pos[i]);
for (int i = 1; i < n; i++)
seg[i] = pos[i + 1] - pos[i];
n --;
seg[0] = Inf;
for (int i = 1; i <= n; i++) {
q.push(i);
pre[i] = i - 1;
nxt[i] = i + 1 > n ? 0 : i + 1;
}
while(k --) {
int tp = q.top(); q.pop();
while(del[tp]) {
tp = q.top();
q.pop();
}
ans += seg[tp];
del[pre[tp]] = 1;
del[nxt[tp]] = 1;
seg[tp] = seg[pre[tp]] + seg[nxt[tp]] - seg[tp];
q.push(tp);
pre[tp] = pre[pre[tp]];
nxt[tp] = nxt[nxt[tp]];
nxt[pre[tp]] = tp;
pre[nxt[tp]] = tp;
}
printf("%lld",ans);
return 0;
}