牛客第十七届上海大学程序设计春季联赛——E CSL 的魔法(贪心)

参考了牛客大佬的代码,真的过了。

贪心策略:
a[]数组第i小去匹配b[]数组第i大的数,这样就能使最终得到的sum最小。因此只要找到a[]数组中第i小和b[]数组中第i大的数一起匹配就好了。(一开始搞错了,去让a[]降序,b[]降序,其实不用这样的)

设置a0[]数组,一开始与a[]数组的元素相同,后将a0[]数组进行降序排序,借助a0[]数组给a[]数组每个元素进行编号,同理,b[]数组也按此方法进行编号。然后,遍历a[],b[]数组,如发现a[i]与b[i]编号不同,则固定b[]数组(或a[]数组),找到a[]中编号与b[i]相同的a[k],a[i]与a[k]进行交换,交换一次,次数cnt++。
然而,最后一步如果按暴力的方式需要两层循环,这样会超时。为了减少一层循环,即减少匹配时间,我们要另设一个数组pos[],pos[i]表示:a[]中编号为i的元素在a[]数组中的位置。这样当需要交换时,我们只需直接将a[i]与a[pos[b[i]]]交换就可以了,不过交换后还要对pos[]进行处理,毕竟此时a[]数组已经发生了变化,需将pos[a[i]]与pos[a[pos[b[i]]]]交换。

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
ll a0[100005],b0[100005],a[100005],b[100005];
ll pos[100005];
bool cmp(ll x,ll y)
{
	return x>y;
}
int find(int x,int y,ll v,int flag)
{
	int m;
	if(flag==1)
	{
		while(x<y)
		{
			m=x+(y-x)/2;
			if(a0[m]==v) break;
			else if(a0[m]>v)
			    y=m;
			else x=m+1;
		}
	}
	else if(flag==2)
	{
		while(x<y)
		{
			m=x+(y-x)/2;
			if(b0[m]==v) break;
			else if(b0[m]>v)
			    x=m+1;
			else y=m;
		}
	}
	return m;
}
int main()
{
	int n,cnt,i,j,temp;
	scanf("%d",&n);
	for(i=0;i<=n-1;i++)
	{
		scanf("%lld",&a0[i]);
		a[i]=a0[i];
	}
	for(i=0;i<=n-1;i++)
	{
		scanf("%lld",&b0[i]);
		b[i]=b0[i];
	}
	sort(a0,a0+n);
	sort(b0,b0+n,cmp);
	for(i=0;i<=n-1;i++)
	{
		temp=find(0,n,a[i],1);
		a[i]=temp;  //a[],b[]里的值没什么用的其实,直接将这两个数组来存储编号就好
		temp=find(0,n,b[i],2);
		b[i]=temp;
	}
	for(i=0;i<=n-1;i++)
	    pos[a[i]]=i;
	cnt=0;
	for(i=0;i<=n-1;i++)
	{
		if(a[i]!=b[i])
		{
			swap(a[i],a[pos[b[i]]]);  //先交换a[i],a[pos[b[i]]]
			swap(pos[a[i]],pos[a[pos[b[i]]]]);  //后交换pos[a[i]],pos[a[pos[b[i]]]],注意,虽然这时a[i],a[pos[b[i]]]的值已经发生变化,但并不影响
			cnt++;
		}
	}
	printf("%d\n",cnt);
	return 0;
}
/*
5
1 5 4 8 9
9 6 4 2 3
2
7
1 9 6 7 3 5 2
11 42 3 5 9 2 8
4
10
1 6 7 4 3 2 9 10 5 8
8 6 10 4 2 7 3 1 5 9
72
1 2
2 12
1 2
2 1*/

此题对本人有较大意义,即在乱序的情况下,通过另外设置一个pos[]数组,记录某一数组编号所在位置,快速的找到编号相同的两个数并交换,需好好掌握。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值