BZOJ 1150: [CTSC2007]数据备份Backup 优先队列+贪心+链表
题解:我们先得到两个楼之间的距离,D[i]表示第i栋楼和第i+1栋楼之间的距离,我们要选出最小的k个数,然后就有两种情况
1.选择了D[i],那么D[i-1]和D[i+1]都不能选择了
2.选择了D[i+1]和D[i-1],然后无法选择D[i].
既要么选D[i]不选D[i-1]和D[i+1],要么选D[i-1]和D[i+1]不选D[i];(怎么证明的也搞不太清楚)我们用优先队列来得到每次最小的距离,
我们发现在选取一个数之后,只对左右两边的数有影响,选了D[i]后,我们把D[i-1]+D[i+1]-D[i]加入队列,同时D[i-1]和D[i+1]不能再选了,我们就把D[i]旁边的边删除,然后再打上标记
和双链表中的删除一个节点类似,这里用数组代替了链表。
还有由于我的优先队列里放的是整个结构体,所以每次取出堆顶后,L和R一定要使用数组中的,而不能直接使用当前取出的节点的L和R(这个地方wa了好几次),因为有些楼的左右节点变了,而优先队列中的还没变。
#include<bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 2e5 + 5; //数组代替链表 每个节点代表一栋楼的信息,以及左边,右边的楼的编号 struct node{ int pos, dis, Lpos, Rpos; node(int pos = 0, int dis = 0, int Lpos = 0, int Rpos = 0){ pos = pos, dis = dis, Lpos = Lpos, Rpos = Rpos; } bool operator<(const node &other)const{ return dis > other.dis; } }a[maxn]; bool vis[maxn]; int b[maxn],n,k; priority_queue<node> que; long long ans; void solve() { while (k--) { while (1) { node cur = que.top(); que.pop(); if (vis[cur.pos]) continue; int pos = cur.pos, Lpos = a[pos].Lpos, Rpos = a[pos].Rpos; //这边这个结构体的左右节点已保存,所以下面的修改顺序没影响 /*优先队列中放整个结构体的一定要注意这个地方,这里Lpos和Rpos一定要用 数组中的L,R,而不能直接使用cur.Lpos,cur.Rpos 我在这里卡了好长时间*/ vis[Lpos] = vis[Rpos] = true; //这个楼已使用,那么它旁边的楼就不能再使用,打上标记,pos这个位置还会继续使用,因此pos不打标记 ans += 1LL*cur.dis; a[pos].dis = a[Lpos].dis + a[Rpos].dis - a[pos].dis;// pos位置继续使用,但改变距离 a[pos].Lpos = a[Lpos].Lpos;
a[pos].Rpos = a[Rpos].Rpos; a[a[Rpos].Rpos].Lpos = pos;
a[a[Lpos].Lpos].Rpos = pos; //改变左右相邻的楼 //这4个修改顺序无影响,左右节点已保存
que.push(a[pos]); break; } } } int main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> b[i]; } for (int i = 1; i < n; i++) { a[i].dis = b[i + 1] - b[i]; a[i].pos = i; a[i].Lpos = i - 1; a[i].Rpos = i + 1; que.push(a[i]); } a[n - 1].Rpos = 0; a[0].dis = a[n].dis = inf; solve(); cout << ans; return 0; }
再来一个不放结构体的
struct cmp{ bool operator()(int a, int b) { return D[a] > D[b]; } }; priority_queue<int, vector<int>, cmp>;
洛谷P1484 种树 和这题类似,选出不超过k个节点,使值最大。