版权声明:本文为博主原创文章,爱转载的表明出处就好. https://blog.csdn.net/qq_36797743/article/details/83314781
题面
题解
早上写了一篇,就还想再写一篇
挺不错的一个题
先来讲讲部分分吧
第一档13分的很简单,直接爆搜就可以了
第二档11分的也很简单,直接排个序就可以了
然后有一档6分的,
,这的话,稍作思考,可以发现,每一次一定是选两个最小的数合起来
然后剩下的档我就不会了。。
算了一下只可以拿到
蛮低的暴力分吧
其实如果你会第三档,
分的,基本上离正解也不远了
先说
分的吧
正解要想到二分答案,然后倒过来做
那么问题就变成了,你一开始有一个数
,然后每一次,你可以把你的数集里面的某一个数
,把他拆成
个数,并且要求拆出来的m个数,都要满足都比
要大。正确性其实挺显然的。
然后你考虑,满分的话
问题就等价于,你拆成n个数以后,可以找到一个对应方案,使得每一个数都比对应的
大
这个做法其实也不错的
我们可以进行讨论
我们把当前数集的值
拿出来,然后看一下目标数集的最大值
,再看一下
的最小值
如果
,那么显然,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;
}