【CTSC 2007】 数据备份
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=1150
【算法】
首先,有一个很显然的结论 : 如果要使距离和最小,必须选择相邻的办公楼配对
问题就转化为了 : 有一个包含(n-1)个数的序列,在这(n-1)个数中选k个,相邻的数不能选,使得和最小
考虑这个序列中最小的元素,我们发现,如果选这个数,那么与它相邻的两个数都不能选,如果不选,那么与它相邻的两个数都要选
因此, 我们可以选出序列中最小的元素Di,将Di-1和Di+1删除,将Di-1+Di+1-Di加入,问题就转化为了在现在的序列中找到(k-1)个数,相邻的数不能选,使得和最小,这样,如果没有选Di-1+Di+1-Di这个数,说明选Di是最优策略,否则说明不是最优策略,
答案正好将Di减去,加上了Di-1+Di+1
那么,堆和链表是可以解决这个问题的
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 200010 const long long INF = 1e10; int i,n,k; int pre[MAXN],nxt[MAXN]; long long s[MAXN],d[MAXN]; bool visited[MAXN]; long long ans,val; struct info { long long d; int pos; } tmp; class Heap { private : int tot; info hp[MAXN]; public : inline bool cmp(info a,info b) { return a.d < b.d; } inline void Up(int x) { if (x == 1) return; int fa = x >> 1; if (cmp(hp[x],hp[fa])) { swap(hp[x],hp[fa]); Up(fa); } } inline void Down(int x) { int son = x << 1; if (son > tot) return; if ((son + 1 <= tot) && (cmp(hp[son+1],hp[son]))) son++; if (cmp(hp[son],hp[x])) { swap(hp[son],hp[x]); Down(son); } } inline void insert(info x) { hp[++tot] = x; Up(tot); } inline void del() { swap(hp[1],hp[tot]); tot--; Down(1); } inline info get() { return hp[1]; } } H; int main() { scanf("%d%d",&n,&k); for (i = 1; i <= n; i++) scanf("%lld",&s[i]); for (i = 1; i < n; i++) d[i] = s[i+1] - s[i]; for (i = 1; i < n; i++) { pre[i] = i - 1; nxt[i] = i + 1; H.insert((info){d[i],i}); } d[0] = d[n] = INF; for (i = 1; i <= k; i++) { tmp = H.get(); while (visited[tmp.pos]) { H.del(); tmp = H.get(); } ans += tmp.d; H.del(); visited[pre[tmp.pos]] = true; visited[nxt[tmp.pos]] = true; d[tmp.pos] = d[pre[tmp.pos]] + d[nxt[tmp.pos]] - tmp.d; nxt[pre[pre[tmp.pos]]] = tmp.pos; pre[tmp.pos] = pre[pre[tmp.pos]]; pre[nxt[nxt[tmp.pos]]] = tmp.pos; nxt[tmp.pos] = nxt[nxt[tmp.pos]]; H.insert((info){d[tmp.pos],tmp.pos}); } printf("%lld\n",ans); return 0; }