uoj #177. 新年的腮雷

版权声明:本文为博主原创文章,爱转载的表明出处就好. https://blog.csdn.net/qq_36797743/article/details/83314781

题面

http://uoj.ac/problem/177

题解

早上写了一篇,就还想再写一篇
挺不错的一个题
先来讲讲部分分吧
第一档13分的很简单,直接爆搜就可以了
第二档11分的也很简单,直接排个序就可以了
然后有一档6分的, m = 2 b = { 1 , 1 } m=2,b=\{1,1\} ,这的话,稍作思考,可以发现,每一次一定是选两个最小的数合起来
然后剩下的档我就不会了。。
算了一下只可以拿到 13 + 11 + 6 = 30 13+11+6=30
蛮低的暴力分吧

其实如果你会第三档, 24 24 分的,基本上离正解也不远了
先说 24 24 分的吧
正解要想到二分答案,然后倒过来做
那么问题就变成了,你一开始有一个数 x x ,然后每一次,你可以把你的数集里面的某一个数 a a ,把他拆成 m m 个数,并且要求拆出来的m个数,都要满足都比 a i a_i 要大。正确性其实挺显然的。
然后你考虑,满分的话
问题就等价于,你拆成n个数以后,可以找到一个对应方案,使得每一个数都比对应的 a i a_i
这个做法其实也不错的
我们可以进行讨论
我们把当前数集的值 x x 拿出来,然后看一下目标数集的最大值 y y ,再看一下 b b 的最小值 b b'
如果 x b < y x-b'<y ,那么显然,y是不可能由别的东西拆开得到了,因此,找一个刚好比他大的数,对应起来即可
否则就把x拆开,这个,其实正确性也挺显然的,因为你拆别的不会比拆这个要优
当你得到了n个数的时候,check一下就可以了
时间复杂度显然是对的
CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
const int N=50005;
int n,m;
int a[N],b[N];
multiset<int> s;
multiset<int> :: iterator it;
bool check (int x)//这个答案行不行 
{
	s.clear();s.insert(x);
	int now=n;
	while (s.size()<now)//现在有多少个数了 
	{
		if (s.empty()) return false;
		it=--s.end();
		if ((*it)-b[1]<a[now])//不行 
		{
			it=s.lower_bound(a[now]);
			if (it==s.end()) return false;
			s.erase(it);now--;
		}
		else
		{
			for (int u=1;u<=m;u++)	
				s.insert((*it)-b[u]);
			s.erase(it);
		}
	}
	int i=1;
	for (it=s.begin();it!=s.end()&&i<=now;it++,i++)
		if (a[i]>(*it)) return false;
	return true;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int u=1;u<=n;u++) scanf("%d",&a[u]);
	for (int u=1;u<=m;u++) scanf("%d",&b[u]);
	sort(b+1,b+1+m);sort(a+1,a+1+n);
	int l=0,r=1000000000,ans=-1;
	while (l<=r)
	{
		int mid=(l+r)>>1;
		if (check(mid))	{r=mid-1;ans=mid;}
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36797743/article/details/83314781