2021牛客暑期多校训练营1 G Game of Swapping Numbers(贪心)

这篇博客探讨了一个算法问题,即如何通过交换数组中的元素最大化两数组元素之差的绝对值总和。当交换次数限制为2时,可以直接判断结果;对于交换次数大于2的情况,可以储存每对数的最大值和最小值,然后贪心地选择能产生最大差值的交换。博主提供了C++代码实现这一策略。
摘要由CSDN通过智能技术生成

题目链接:点击这里

题目大意:
给定两个长度为 n n n 的数组, a i , b i a_i,b_i ai,bi ,交换 k k k a a a 中的元素,使得 ∑ i = 1 n ∣ a i − b i ∣ \sum_{i=1}^n|a_i-b_i| i=1naibi 最大,输出该最大值

题目分析:
假设有两对数 ( a i , b i ) , ( a j , b j ) (a_i,b_i),(a_j,b_j) (ai,bi),(aj,bj) ,则不交换使该数对的价值为 ∣ a i − b i ∣ + ∣ a j − b j ∣ |a_i-b_i|+|a_j-b_j| aibi+ajbj
不妨设 a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj
b i > a j b_i>a_j bi>aj ,交换前为 ∣ a i − b i ∣ + ∣ a j − b j ∣ = a i + a j − b i − b j |a_i-b_i|+|a_j-b_j|=a_i+a_j-b_i-b_j aibi+ajbj=ai+ajbibj
交换后为 ∣ a j − b i ∣ + ∣ a i − b j ∣ = b i − a j + a i − b j |a_j-b_i|+|a_i-b_j|=b_i-a_j+a_i-b_j ajbi+aibj=biaj+aibj ,结果之差为 2 b i − 2 a j = 2 m i n ( a i , b i ) − 2 m a x ( a j , b j ) 2b_i-2a_j=2min(a_i,b_i)-2max(a_j,b_j) 2bi2aj=2min(ai,bi)2max(aj,bj)
b i < a j b_i<a_j bi<aj ,交换前为 ∣ a i − b i ∣ + ∣ a j − b j ∣ = a i + a j − b i − b j |a_i-b_i|+|a_j-b_j|=a_i+a_j-b_i-b_j aibi+ajbj=ai+ajbibj
交换后为 ∣ a j − b i ∣ + ∣ a i − b j ∣ = a j − b i + a i − b j |a_j-b_i|+|a_i-b_j|=a_j-b_i+a_i-b_j ajbi+aibj=ajbi+aibj ,结果之差为 0 0 0
由此我们发现可以产生贡献的交换是一对数的最小值大于另一对数的最大值的情况。
我们又发现,在 k = 2 k=2 k=2 时交换情况是唯一的,我们必须把交换次数用完;而 k > 2 k>2 k>2 时,我们可以选取交换代价为 0 0 0 的组来抵消交换次数,因此在 k > 2 k>2 k>2 时交换 k k k 次是可以转化为交换小于等于 k k k 次来处理。
所以在 k = 2 k=2 k=2 时,我们直接暴力判断结果即可;在 k > 2 k>2 k>2 时,我们就可以分别把每一对数的最值存下来,然后排个序,贪心的取:让大的最小值和小的最大值交换即可

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#define ll long long
#define inf 0x3f3f3f3f
//#define int  ll
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e6+5;
//const int mod = 9973;
const double pi = acos(-1);
const double eps = 1e-8;
int n,k,a[maxn],b[maxn],maxx[maxn],minn[maxn];

int main()
{
	n = read(),k = read();
	for(int i = 1;i <= n;i++) a[i] = read();
	for(int i = 1;i <= n;i++) b[i] = read();
	ll ans = 0;
	if(n == 2)
	{
		if(k%2) swap(a[1],a[2]);
		ans = abs(a[1]-b[1])+abs(a[2]-b[2]);
	}
	else {
		for(int i = 1;i <= n;i++)
		{
			ans += abs(a[i]-b[i]);
			maxx[i] = max(a[i],b[i]);
			minn[i] = min(a[i],b[i]);
		}
		sort(maxx+1,maxx+n+1);
		sort(minn+1,minn+n+1,greater<int>());
		for(int i = 1;i <= k && i<= n;i++)
			if(minn[i] > maxx[i]) ans += 2*(minn[i]-maxx[i]);
			else break;
	}
	cout<<ans<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值