牛客 - Sixth Sense(贪心+二分)

题目链接:点击查看

题目大意:给出两个长度为 n 的数列记为 a 和 b,现在 a 的数列固定不动,问如何对数列 b 进行排列,可以使得:

  1. b[ i ] > a[ i ] (严格大于)的位置尽可能多
  2. 在满足上述要求的前提下,b 的排列字典序最大

题目分析:如果不考虑第二个约束的话,那就是一个非常简单的贪心问题了,贪心策略如下:因为最终需要的是 a 中的每个元素和 b 中每个元素一一对应的一个结果,所以只需要考虑其相对位置,将两个数组分别排序,然后用双指针一个去枚举 a 中的元素,另一个贪心从 b 中寻找元素与其匹配即可

如果再考虑上第二个约束该如何去做呢,因为 n 只有 5e3 的级别,可以考虑正向遍历一遍每个元素去确定 ans[ i ] 的值,此时不难想到一个 n^3 的做法了:

  1. 首先求出不受约束 2 限制的答案,记为 res
  2. 第一层枚举 i ,表示当前正在确定 ans[ i ] 的值
  3. 第二层枚举 j ,表示第 i 个位置放置 b 中的第 j 个元素 ,即 ans[ i ] = b[ j ]
  4. 最后一层循环去检查第 ans[ i ] = b[ j ] 时的答案,最后从贡献为 res 的 b[ j ] 中选出最大的 b[ j ] 放在 ans[ i ] 即可

考虑优化时间复杂度,第一层枚举的 i 无法优化,第三层检查的循环也不好优化,所以突破点就在第二层枚举的 j,我们先暂时搁置一下

到这里可能会有疑惑了,因为最开始说的,贪心策略去求解答案的话,是需要进行排序的,所以上述算法的时间复杂度不应该是 n^3*logn 的嘛,其实并不需要每次都排序,只需要初始时额外维护一个递增的数组 aa 和数组 bb,每次操作完位置 i 后将 a[ i ] 和 b[ j ] 中相应的元素删除掉即可,时间复杂度不过也才 O( n ) ,这样就将每次贪心求解答案的时间控制在了 O( n )

继续讨论如何优化掉第二层的 j,假设第一层枚举到了 i,此时 a 数组中对应的元素是 a[ i ] ,将 bb 数组(有序)中的元素分成两部分:

  1. 小于等于 a[ i ] 的
  2. 大于 a[ i ] 的

不难看出两个部分中的 bb 元素分别都是具有单调性的,具体指的是,对于情况一来说,如果 bb[ i ] 放在这里合适,那么 bb[ i + 1 ] 放在这里也是合适的,对于情况二来说,如果 bb[ i ] 放在这里合适,那么 bb[ i - 1 ] 放在这里也是合适的,如此就可以将枚举的时间复杂度优化成二分了,最后总的时间复杂度为O( n^2logn )

代码:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map> 
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=5e3+100;

int a[N],b[N],n;

vector<int>bb,aa;

int cal(int ban1,int ban2)//去掉aa[ban1]和bb[ban2]后的结果 
{
	int ans=0;
	int i=0,j=0;
	while(i<aa.size()&&j<bb.size())
	{
		if(i==ban1)
		{
			i++;
			continue;//检查一下是否越界 
		}
		if(j==ban2)
		{
			j++;
			continue;//同上 
		}
		if(aa[i]<bb[j])
		{
			ans++;
			i++;
		}
		j++;
	}
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",a+i);
		aa.push_back(a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",b+i);
		bb.push_back(b[i]);
	}
	sort(aa.begin(),aa.end());
	sort(bb.begin(),bb.end());
	int ans=cal(-1,-1);
	for(int i=1;i<=n;i++)
	{
		int pos1=-1;//定位,aa[pos1]=a[i]
		for(int j=0;j<aa.size();j++)
			if(aa[j]==a[i])
				pos1=j;
		int l=upper_bound(bb.begin(),bb.end(),a[i])-bb.begin(),r=bb.size()-1,mark=-1;
		while(l<=r)
		{
			int mid=l+r>>1;
			if(cal(pos1,mid)==ans-1)
			{
				mark=mid;
				l=mid+1;
			}
			else
				r=mid-1;
		}
		if(mark==-1)
		{
			int l=0,r=upper_bound(bb.begin(),bb.end(),a[i])-bb.begin()-1;
			while(l<=r)
			{
				int mid=l+r>>1;
				if(cal(pos1,mid)==ans)
				{
					mark=mid;
					l=mid+1;
				}
				else
					r=mid-1;
			}
		}
		printf("%d ",bb[mark]);
		ans-=(bb[mark]>aa[pos1]);
		aa.erase(aa.begin()+pos1);
		bb.erase(bb.begin()+mark);
	}









   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108943266