BZOJ 2151 种树(可反悔贪心,链表)BZOJ千题计划就图一乐
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2151 种树(可反悔贪心,链表)BZOJ千题计划就图一乐相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
题目链接
https://hydro.ac/d/bzoj/p/2151
Problem
A 城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出
n
n
n 个种树的位置,顺时针编号
1
1
1 到
n
n
n 。并且每个位置都有一个美观度
a
i
a_i
ai ,如果在这里种树就可以得到这
a
i
a_i
ai 的美观度。但由于 A 城市土壤肥力欠佳,两棵树决不能种在相邻的位置(
i
i
i 号位置和
i
+
1
i+1
i+1 号位置叫相邻位置。值得注意的是
1
1
1 号和
n
n
n 号也算相邻位置!)。最终市政府给园林部门提供了
m
m
m 棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将
m
m
m 棵树苗全部种上,输出 Error!
。
n ≤ 2 × 1 0 5 n\\le 2\\times 10^5 n≤2×105, m ≤ n m\\leq n m≤n , − 1 0 3 ≤ a i ≤ 1 0 3 -10^3\\leq a_i\\leq 10^3 −103≤ai≤103 。
Solution
这道题一眼就可以看出来显然是可以贪心的,大致是一个带反悔的贪心。
考虑维护一个大根堆,从所有坑中选取价值最大的,使用这个坑。
但是当前最优不一定保证局面最优,可能当前这个坑 x x x 的价值虽然是最大的,但它两边的坑的价值之和 w l + w r w_l+w_r wl+wr 要大于中间的这个坑的价值 w x w_x wx,所以我们需要提供一次反悔的机会,选择中间的坑之后,若选择两边的坑,显然要丢掉中间的这个坑,得到的权值 z = w l + w r − w x z=w_l+w_r-w_x z=wl+wr−wx,我们将 z z z 丢进大根堆里,继续维护最优值即可。
题目要求选择坑 x x x ,它左右相邻的两个坑就不能使用了,所以我们可以建一个双向链表维护一下每个结点的前驱和后继,在删点的时候把链表合并一下就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> PII;
using ll = long long;
const int maxn = 3e5 + 6;
int n, m, s, t;
ll ans;
int pre[maxn], nex[maxn];
int w[maxn];
priority_queue <PII> q;
bool vis[maxn];
void change(int x)
{
vis[x] = 1;
pre[nex[x]] = pre[x];
nex[pre[x]] = nex[x];
pre[x] = nex[x] = 0;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i)
scanf("%d", &w[i]), q.push({w[i], i});
if(n / 2 < m)
return 0 * puts("Error!");
nex[n] = 1, pre[1] = n;
for (int i = 1; i < n; ++ i) nex[i] = i + 1;
for (int i = 2; i <= n; ++ i) pre[i] = i - 1;
while(m -- ) {
while(vis[q.top().second])
q.pop();
PII x = q.top();
q.pop();
ans += x.first;
int pos = x.second;
int l = pre[pos], r = nex[pos];
w[pos] = w[l] + w[r] - w[pos];
change(l), change(r);
q.push({w[pos], pos});
}
cout << ans << endl;
return 0;
}
以上是关于BZOJ 2151 种树(可反悔贪心,链表)BZOJ千题计划就图一乐的主要内容,如果未能解决你的问题,请参考以下文章