Codeforces Round #274(Div2) B. Towers 贪心

解法一:复杂度为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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值