Codeforces Round #374 (Div. 2) D. Maxim and Array 贪心+优先队列

题意:

给定 n 个整数,有 k 次机会可以使任意某个数增加或减少 x

问使得最后n个数乘积最小的序列是什么

思路:

首先要想到序列中可能存在负数

负数为负数时:整个序列乘积为负,要是序列乘积更小,我们可以找一个负数-x,也可以找一个正数+x

这时候就要考虑找哪个数:

假设有m个正数,ans = a1*a2*....*am  如果要选择一个数-x,让这个式子变小,我们暴力每一个数的话,可以知道:如果ai 减去x的话,那整个序列相当于减去 x*(ans / ai ) , x ,ans 都是定值,所以要选择ai 尽量小的数;

而本题也可以把这个序列的每个数的绝对值看成上述这个正数式子,无论负数的个数为奇数还是偶数,我们只需要考虑+x 或者-x,但是最优情况一定是选择绝对值最小的数操作

具体可以分情况 自己分析一下

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <set>

using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int maxn = 2e5 + 7;

int n, k, x;
set<P> st;
ll a[maxn];

int main() {
  scanf("%d%d%d", &n, &k, &x);
  int t = 0; // 记录负数个数
  for(int i = 1; i <= n; ++i) {
    scanf("%lld", &a[i]);
    if(a[i] < 0) t ^= 1;
    st.insert(P(abs(a[i]),i));
  }
  for(int i = 0; i < k; ++i) {
    int pos = st.begin()->second;
    st.erase(st.begin());
    if(a[pos] < 0) t ^= 1;
    if(t) a[pos] += x;
    else a[pos] -= x;
    if(a[pos] < 0) t ^= 1;
    st.insert(P(abs(a[pos]),pos));
  }
  for(int i = 1; i <= n; ++i) {
    printf("%lld ", a[i]);
  }

  return 0;
}

猜你喜欢

转载自blog.csdn.net/xiang_6/article/details/81531713