Description
A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。
思路
最近刚看了一下反悔贪心, 再做这题, 发现最难的还是要证明贪心的正确性,和构造正确的反悔贪心。
显然我们每次考虑当前的最大权值的点种与不种, 如 T Q A B C W E 7点中,B点权值最大, 但如果我们选了B点, 就无法选择 A C两点了, 就影响了 A B C 三点, Q,W可以随便选择
如果我们不选择 B 点 是否一定会选A, C 两点呢,
1, 显然 A, C不可能同时不选, 因为同时不选, 那么肯定会选B, 使结果更优秀。
2, A, C只选一个, 如果只选 C, 那么就不能选W, 此时的状况是 A B C W 的状态确定了, 只选择了C, 而没有选择 A B W,此时 T,E 不受影响, 显然此时和只选择B的情况是可以比较的 Q, W 就开始不受影响了,
显然如果W[C]< W[B], 这么选不会使结果更优(条件是 W[b]>W[c] && W[b] > W[A])
3,A C都选, 就有可能比只选择B更优秀。
做法, 用优先队列每次弹出可选的点中间的权值最大的点, 选出来以后再加入一个新点, 该点融合了ABC
三个点(设选了B) 权值为 W[A] + W[c] - W[b], 这样以后我们还可能用到这个点,如果以后再用到这个点, 就等于说后悔不选B了,而去选AC两点了
具体细节见于代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int last[N], Next[N], vis[N], a[N];
int sum, n, k, ans;
struct node
{
int w, id;
bool operator <(const node &t) const
{
return w < t.w;
}
};
priority_queue<node> q;
void build()
{
for(int i = 1; i <= n; i ++)
{
int x;
scanf("%lld", a + i);
x = a[i];
last[i] = i - 1;
Next[i] = i + 1;
if(i == 1) last[i] = n;
if(i == n) Next[i] = 1;
q.push({x, i});
}
}
void change(int x)
{
vis[x] = true;
Next[last[x]] = Next[x];
last[Next[x]] = last[x];
last[x] = Next[x] = 0;
}
void work()
{
node x;
for(int i = 1; i <= k; i ++)
{
while(vis[q.top().id]) q.pop();
x = q.top();
// cout << x.w << endl;
q.pop();
ans += x.w;
int l = last[x.id], r = Next[x.id];
a[x.id] = a[l] + a[r] - a[x.id];
node now = (node){a[x.id], x.id};
change(l), change(r);
q.push(now);
}
}
signed main()
{
scanf("%lld%lld", &n, &k);
build();
if(k > n / 2)
{
cout << "Error!" << endl;
return 0;
}
work();
cout << ans << endl;
return 0;
}