数据备份
题目链接:luogu P3620
题目大意
给你一个数轴上面的一些点,你要选若干个点对,使得每个点至多在一个点对中,而且要你最小化每个点对之间距离的和。
思路
首先考虑普通的贪心。
就是每次选费用最小的那个,然后把它两边可以选的删掉。
但是你可能选旁边两个而不选它更优,所以考虑一个撤回操作:
你选了之后删掉两个旁边的和自己之后,加入旁边两个减去自己的值,那选了这个就抵消了这次选自己的,然后选了旁边的。(总体来讲也是多选了一个)
然后因为你旁边可能早就被删了,那你要找到的是旁边第一个没有被删的,所以我们可以用双向链表来维护。
代码
#include<queue>
#include<cstdio>
#define ll long long
using namespace std;
struct pl {
int val, l, r;
}a[100001];
int n, k, x, lst;
bool cnot[100001];
ll ans;
struct node {
int pl, val;
};
bool operator <(node x, node y) {
return x.val > y.val;
}
priority_queue <node> q;
void Delete(int now) {//删掉它两边的点
a[a[a[now].l].l].r = now;
a[a[a[now].r].r].l = now;
a[now].l = a[a[now].l].l;
a[now].r = a[a[now].r].r;
}
int main() {
scanf("%d %d %d", &n, &k, &lst);
for (int i = 1; i < n; i++) {
scanf("%d", &x);
a[i].l = i - 1;
a[i].r = i + 1;
a[i].val = x - lst;
q.push((node){i, a[i].val});
lst = x;
}
a[0].val = a[n].val = 1e9 + 1e7;//注意边界是要尽可能不优
for (int i = 1; i <= k; i++) {
while (cnot[q.top().pl]) q.pop();
node now = q.top();
q.pop();
ans += now.val;
cnot[a[now.pl].l] = cnot[a[now.pl].r] = 1;
a[now.pl].val = a[a[now.pl].l].val + a[a[now.pl].r].val - a[now.pl].val;//反悔的花费
q.push((node){now.pl, a[now.pl].val});
Delete(now.pl);
}
printf("%lld", ans);
return 0;
}