【luogu P3620】数据备份(反悔贪心 / 撤回贪心)

数据备份

题目链接: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;
}
上一篇:ARTS打卡计划第三周-Tips


下一篇:Flask实战-留言板-使用Faker生成虚拟数据