uoj #177. 新年的腮雷

題面

http://uoj.ac/problem/177

題解

早上寫了一篇,就還想再寫一篇
挺不錯的一個題
先來講講部分分吧
第一檔13分的很簡單,直接爆搜就可以了
第二檔11分的也很簡單,直接排個序就可以了
然後有一檔6分的,m=2b={1,1}m=2,b=\{1,1\},這的話,稍作思考,可以發現,每一次一定是選兩個最小的數合起來
然後剩下的檔我就不會了。。
算了一下只可以拿到13+11+6=3013+11+6=30
蠻低的暴力分吧

其實如果你會第三檔,2424分的,基本上離正解也不遠了
先說2424分的吧
正解要想到二分答案,然後倒過來做
那麼問題就變成了,你一開始有一個數xx,然後每一次,你可以把你的數集裏面的某一個數aa,把他拆成mm個數,並且要求拆出來的m個數,都要滿足都比aia_i要大。正確性其實挺顯然的。
然後你考慮,滿分的話
問題就等價於,你拆成n個數以後,可以找到一個對應方案,使得每一個數都比對應的aia_i
這個做法其實也不錯的
我們可以進行討論
我們把當前數集的值xx拿出來,然後看一下目標數集的最大值yy,再看一下bb的最小值bb'
如果xb<yx-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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章