【hiho一下_week257】A Box of Coins

本文详细解析了使用贪心算法解决硬币移动问题的方法。通过分析单排和双排硬币移动的最优策略,介绍了如何在限定时间内使每个单元格中的硬币数量相等,最小化移动次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hihocoder的hiho一下,第257周的题目



原题

传送门:https://hihocoder.com/contest/hiho257/problem/1

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

Little Hi has a box which consists of 2xN cells as illustrated below.

±—±---±—±---±—±---+
| A1 | A2 | A3 | A4 | … | AN |
±—±---±—±---±—±---+
| B1 | B2 | B3 | B4 | … | BN |
±—±---±—±---±—±---+
There are some coins in each cell. For the first row the amounts of coins are A1, A2, … AN and for the second row the amounts are B1, B2, … BN.

Each second Little Hi can pick one coin from a cell and put it into an
adjacent cell. (Two cells are adjacent if they share a common side.
For example, A1 and A2 are adjacent; A1 and B1 are adjacent; A1 and B2
are not adjacent.)

Now Little Hi wants that each cell has equal number of coins by moving
the coins. He wants to know the minimum number of seconds he needs to
accomplish it.

输入

The first line contains an integer, N. 2 <= N <= 100000

Then follows N lines. Each line contains 2 integers Ai and Bi. (0 <=
Ai, Bi <= 2000000000)

It is guaranteed that the total amount of coins in the box is no more
than 2000000000 and is a multiple of 2N.

输出

The minimum number of seconds.


我的思路

一开始理解错了题目的意思,以为每个cell中的coins是整体,要一起移动,然后,就不会做了。。


解析及看了解析之后的答案

  • 解析

本题是一道贪心的题目。

我们不妨先思考一下只有一排的情况。假设只有只有一排是A1, A2, … AN,硬币只能左右相邻移动,并且平均值是ave。

这时考虑A1,因为A1只能和A2交换硬币。所以如果A1 >= ave,那么一定需要将(A1 -ave)个硬币从A1移动到A2;反之,如果A1 <= ave,那么一定需要将(ave - A1)个硬币从A2移动到A1。

注意上述移动是一定会发生的,不然A1不可能变成ave。而移动之后A1’ = ave,A2’ = A2 + (A1 - ave)。

这时A1’不会再发生变化,A2’也只能跟A3交换硬币,于是我们可以重复上述的贪心策略。直到所有格子里的硬币都是ave,这时总共移动的硬币数就是答案。

当扩展的2排的时候。我们仍然可以从左向右考虑。

首先考虑A1和B1。它们除了两者之间能交换硬币之外,只能同A2和B2交换。

与1排时的分析类似,如果A1 >= ave且B1 >= ave,那么一定是将(A1 - ave)个个硬币从A1移动到A2并且将(B1 -ave)个个硬币从B1移动到B2。 先A1与B1之间移动若干硬币,再将多余的硬币移动到A2B2的方案一定不会是最优的。

同理如果A1 <= ave且B1 <= ave,那么一定是将(ave - A1)个个硬币从A2移动到A1并且将(ave -B1)个个硬币从B2移动到B1。 先A1与B1之间移动若干硬币,再从A2B2补充硬币的方案一定不会是最优的。

最后,如果A1,B1其中一个大于ave一个小于ave,不妨设A1 > ave且B1 < ave;那么应当A1先移动 min(A1 -ave, ave - B1) 枚硬币至B1。然后如果A1有盈余则移动到A2,如果B1有不足则从B2补充。

  • 代码
    需要注意的是,每次只能移动一个coin

      #include <iostream>
      #include <cmath>
      #include <algorithm>
      using namespace std;
      
      /* https://hihocoder.com/contest/hiho257/problem/1 */
      
      int main(int argc, char** argv) {
      	int n;
      	long long int data[2][100001];
      	int i =0;
      	long long int total = 0;
      	long long int ans = 0;
      	long long int ave;
      	scanf("%lld", &n);
      	
      	for (i = 0; i < n; i++) {
      		scanf("%lld %lld", &data[0][i], &data[1][i]);
      		total += (data[0][i] + data[1][i]);
       	}
       	ave = total / (2*n);
       	
      	for (i = 0; i < n; i++) {
      		if (data[0][i] > ave) {
      			// A[i] 超过了平均值,就先匀给B[i]
      			long long int res = data[0][i] - ave;
      			if (data[1][i] < ave && res >0) {
      				long long int temp = min(res, ave-data[1][i]);
      				// 匀过去,同时更新res、A[i]、B[i]
      				data[1][i] += temp;
      				data[0][i] -= temp;
      				res = res - temp;
      				ans+=temp;
      			}
      			// 匀完还有,再匀给A[i+1] 
      			if (res > 0) {
      				// 匀过去,同时更新A[i+1]、A[i]
      				data[0][i+1] += res;
      				data[0][i] = ave;
      				ans+=res;
      			}	
      		}
      		// B[i] 超过平均值
      		if (data[1][i] > ave ) {
      			// A[i] 超过了平均值,就先匀给B[i]
      			long long int res = data[1][i] - ave;
      			if (data[0][i] < ave && res >0) {
      				long long int temp = min(res, (ave-data[0][i]));
      				// 匀过去,同时更新res、A[i]、B[i]
      				data[0][i] += temp;
      				data[1][i] -= temp;
      				res = res - temp;
      				ans+=temp;
      			}
      			// 匀完还有,再匀给A[i+1] 
      			if (res > 0) {
      				// 匀过去,同时更新A[i+1]、A[i]
      				data[1][i+1] += res;
      				data[1][i] = ave;
      				ans+=res;
      			}	
      		}
      		
      		// 不够average的情况
      		if (data[0][i]<ave)
               {
                   long long int need = ave-data[0][i];
                   if (need>0)
                   {
                       data[0][i+1]-=need;
                       ans+=need;
                       data[0][i]=ave;
                   }
               }
               if (data[1][i]<ave)
               {
                   long long int need = ave-data[1][i];
                   if (need>0)
                   {
                       data[1][i+1] -= need;
                       ans+=need;
                       data[1][i]=ave;
                   }
               }
      	}
      	
      	printf("%lld\n", ans);
      	return 0;
      }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值