Codeforces Round #629 (Div. 3) (A ~ F)
被强♂制♂上任的第一天
F. Make k Equal
题目-> http://codeforces.com/contest/1328/problem/F
题意:
给你 n 个数 , 通过任意次操作:
①最大数 - 1
②最小数 + 1
保证数组中有k个数相同 , 问最小操作次数
分析:
如果已经有k个相同的输出0,否则:
假设最终有k个a (首先明确这里的a 一定是输入数组的其中一个值 ,这样一定可以保证最优解), 那么对于任意数b ,
当b < a , 只有把所有小于a的数都变成a - 1 , 然后 再取其中的一个数 + 1 ,即可以得到一个a
当b > a ,只有把所有大于a的数都变成a + 1,然后 再取其中一个数 - 1, 即可以得到一个a
所以 , 先排序 ,接下来,枚举每个值是最优解的a , 然后a可以通过 比他小值 和 比他大的值 转移过来 , 从而求出解,详细看代码。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; #define ll long long #define ios std::ios::sync_with_stdio(false) const ll INF(0x3f3f3f3f3f3f3f3fll); #define int long long typedef unsigned long long ULL; map<int , int>ma; int a[maxn]; int b[maxn];///存不同的数 int c[maxn];///存每个不同数的个数 int pre[maxn] , pre_sum[maxn];///前缀个数,前缀和 int nex[maxn] , nex_sum[maxn];///后缀个数,后缀和 signed main() { ios; cin.tie(0);/// int n , k; cin >> n >> k; int cnt = 0; for(int i = 1 ; i <= n ; i ++){ cin >> a[i]; ma[a[i]] ++; cnt = max(cnt , ma[a[i]]);///记录最多的数的数量 } if(cnt >= k)return cout << 0 << '\n' , 0; sort(a + 1 , a + 1 + n); cnt = 1; int sum = 1; for(int i = 1 ; i <= n ; i ++){ if(a[i] == a[i + 1]) sum ++; else{ b[cnt] = a[i];///记不同的数,这里用map记也可以 c[cnt] = sum;///这个数有几个 cnt ++ , sum = 1; } } n = cnt - 1; for(int i = 1 ; i <= n ; i ++)/// 前缀个数和前缀和 pre[i] = pre[i - 1] + c[i] , pre_sum[i] = pre_sum[i - 1] + c[i] * b[i]; for(int i = n ; i >= 1 ; i --) nex[i] = nex[i + 1] + c[i] , nex_sum[i] = nex_sum[i + 1] + c[i] * b[i]; int ans = INF; for(int i = 1 ; i <= n ; i ++){ int now = k - c[i];///缺多少个数 if(now > pre[i - 1]) ///如果前面的数不够的话,后面也要拿 ans = min(ans , pre[i - 1] * (b[i] - 1) - pre_sum[i - 1] + nex_sum[i + 1] - nex[i + 1] * (b[i] + 1 ) + now); else ///前面的数都变成b[i] - 1 ,然后再变now个 , ans = min(ans , pre[i - 1] * (b[i] - 1) - pre_sum[i - 1] + now); if(now > nex[i + 1]) ans = min(ans , pre[i - 1] * (b[i] - 1) - pre_sum[i - 1] + nex_sum[i + 1] - nex[i + 1] * (b[i] + 1 ) + now); else ans = min(ans , nex_sum[i + 1] - nex[i + 1] * (b[i] + 1) + now); } cout << ans << '\n'; return 0; }
待补