1. 程式人生 > >uoj #177. 新年的腮雷

uoj #177. 新年的腮雷

題面

題解

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

其實如果你會第三檔,2424分的,基本上離正解也不遠了 先說2424分的吧 正解要想到二分答案,然後倒過來做 那麼問題就變成了,你一開始有一個數xx,然後每一次,你可以把你的數集裡面的某一個數a

a,把他拆成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; }