【NOIP模擬】購物
阿新 • • 發佈:2018-11-07
題面
雙11就要來啦!Yuno 剛剛獲得了X 元的獎金。那麼是不是應該清空下購物車呢?
購物車總共有 N 個物品,每個物品的價格為 Vi ,Yuno 想盡可能地把獎金給花光,所以她要精心選擇一些商品,使得其價格總和最接近但又不會超過獎金的金額。那麼 Yuno 最後最少可以剩下多少錢呢?
10% 的資料:N ≤ 10
40% 的資料:N ≤ 20, X,Vi ≤ 10000
100% 的資料:N ≤ 40, X,Vi ≤ 109
分析
40%的資料可以01揹包做,而220=10242說明也可以暴搜,即列舉子集來做。
其實可以發現40=20+20,這說明什麼?分成兩半來搜就可以了。於是我們想到了用折半搜尋,搜尋樹的深度減小一半,也就只有220
關鍵是需要meet in mid,怎麼meet呢?對於第二次搜尋的每一個得到的子集,我們在第一個搜尋得到的子集中找一個對應的,使這兩個加起來小於X並且最大。
而第一個搜尋得到的子集排序,二分就可以查到了。於是時間複雜度是220*log220 也就是20*220,還是可以接受
程式碼
#include<bits/stdc++.h> using namespace std; #define N 11000000 #define ll long long ll w[N],tmp[N]; ll n,x,pos,cnt,ans,mid; template<classT> inline void read(T &x) { x=0;ll f=1;static char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } inline void dfs1(ll k,ll now) { if(now>x)return; ans=min(ans,x-now);if(k>mid) { tmp[++cnt]=now; return ; } dfs1(k+1,now);dfs1(k+1,now+w[k]); } inline void dfs2(ll k,ll now) { if(now>x)return; if(k>n) { pos=upper_bound(tmp+1,tmp+1+cnt,x-now)-tmp; if(pos) ans=min(ans,x-now-tmp[pos-1]); return ; } dfs2(k+1,now);dfs2(k+1,now+w[k]); } int main() { read(n),read(x);mid=(n+1)/2;ans=x; for(ll i=1;i<=n;i++)read(w[i]); dfs1(1,0); sort(tmp+1,tmp+1+cnt); if(mid+1<=n)dfs2(mid+1,0); printf("%lld\n",ans); return 0; }