解法一:复杂度为O(nk)
每次从最大值向最小值移动1个
代码如下:
#include <cstdio>
using namespace std;
#define N 1005
int a[105], ans[N][2];
int main(){
int n, k, ops = 0, max_val, min_val;
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
while(ops <= k){
max_val = 0, min_val = 10005;
int max_idx, min_idx;
for(int i = 1; i <= n; i ++){
if(max_val < a[i])
max_val = a[i], max_idx = i;
if(min_val > a[i])
min_val = a[i], min_idx = i;
}
if(max_val - min_val < 2 || ops == k)
break;
ans[ops][0] = max_idx, ans[ops][1] = min_idx;
a[max_idx] --, a[min_idx] ++;
ops ++;
}
printf("%d %d\n", max_val - min_val, ops);
for(int i = 0; i < ops; i ++)
printf("%d %d\n", ans[i][0], ans[i][1]);
return 0;
}
本题中n范围较小,所以O(nk)的复杂度没问题
解法二:复杂度为O(nlogn)
思路:在有序的数组中将最大值向最小值移动1,可以保证数组仍然有序。
首先排一次序,记录每个高度的tower个数。每次从数组的末端区间(最大值处)向前端区间(最小值处)移动1。目的是让max_val减小,min_val增大。整个过程中记录下最大值的起点upper和最小值的起点lower,根据最大值的个数more和最小值的个数less的大小关系讨论。
①当more > less时,说明最大值的tower数目比最小值的tower数目多,则最小值的tower全部可以增加一个高度,新的高度仍为最小值,则lower = 0,min_val增加1;此时只有less个最大tower的高度减小了1,下次应从第upper - less个tower开始减小高度
②当more < less时,说明最小值的tower数目较多,则最大值的tower全部可以减小一个高度,新的高度仍为最大值,则upper = n - 1,max_val减小1;此时只有more个最小tower的高度增加了1,下次应从第lower + more个tower开始增加高度
③当more = less时,说明每一个最大值的tower都有一个最小值的tower可以移动过去,从而max_val和min_val向理想的方向靠近,lower和upper都回到数组的首尾两端
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10005
struct Node{
int val, idx;
friend bool operator< (const Node& a, const Node& b){
return a.val < b.val;
}
}towers[105];
int cnt[N], ans[1005][2], ops = 0, k;
void fill(int fill_cnt, int upper, int lower){ //从最大值向最小值移动1
int j = 0;
while(j < fill_cnt && ops < k){
ans[ops][0] = towers[upper - j].idx, ans[ops][1] = towers[lower + j].idx;
ops ++, j ++;
}
}
int main(){
int n;
scanf("%d %d", &n, &k);
for(int i = 0; i < n; ++i){
int x;
scanf("%d", &x), cnt[x] ++;
towers[i].val = x, towers[i].idx = i + 1;
}
sort(towers, towers + n);
int max_val = towers[n - 1].val, min_val = towers[0].val;
int upper = n - 1, lower = 0;
while(ops < k && max_val - min_val > 1){ //最大值和最小值隔1时,最优解已无法减小
int more = cnt[max_val], less = cnt[min_val];
if(more > less && ops + less <= k){ //最小的tower都增加了1个高度
fill(less, upper, lower);
cnt[++min_val] += less, cnt[max_val] -= less, cnt[max_val - 1] += less;
upper -= less, lower = 0;
}
else if(more < less && ops + more <= k){ //最高的tower都减小了1个高度
fill(more, upper, lower);
cnt[--max_val] += more, cnt[min_val] -= more, cnt[min_val + 1] += more;
lower += more, upper = n - 1;
}
else{
if(ops + more <= k)
cnt[--max_val] += more, cnt[++min_val] += less;
fill(more, upper, lower);
upper = n - 1, lower = 0;
}
}
printf("%d %d\n", max_val - min_val, ops);
for(int i = 0; i < ops; i ++){
printf("%d %d\n", ans[i][0], ans[i][1]);
}
return 0;
}