【luogu P3620】資料備份(反悔貪心 / 撤回貪心)
阿新 • • 發佈:2021-11-13
給你一個數軸上面的一些點,你要選若干個點對,使得每個點至多在一個點對中,而且要你最小化每個點對之間距離的和。
資料備份
題目連結: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; }