題解 P4799 【[CEOI2015 Day2]世界冰球錦標賽】
阿新 • • 發佈:2020-11-30
P4799 【[CEOI2015 Day2]世界冰球錦標賽】 (折半搜尋)
part1 40points
暴力的40分是很好寫的,直接搜就行
#include<iostream> #include<cstdio> #include<cstring> #include<string> #define int long long using namespace std; const int maxn=1e6; int n,m; int a[maxn]; int ans=1; void dfs(int x,int la){ if(la-a[x]<0){ return ; } ans++; for(int i=x+1;i<=n;i++){ dfs(i,la-a[x]); } return ; } signed main(){ // ios::sync_with_stdio(false); // freopen("a.in","r",stdin); cin>>n; cin>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ dfs(i,m); } cout<<ans; return 0; }
pasrt2 100points
我們來考慮100分的做法,很明顯,暴力是無法處理這麼大的資料的我們
來考慮對其進行優化,這裡就要折半搜尋了
折半搜尋是針對暴力搜尋的優化演算法,本質上來說就是把搜尋區間分為兩
半,分開計算來優化時間複雜度,折半搜尋的條件是分開搜尋的結果可以
進行合併,針對此題,我們可以將查詢區間一分為二, \(1- mid\) , \(mid+1 -n\)
分別進行暴力搜尋,我們用 \(a,b\) 陣列儲存兩次搜尋的結果,即每個區間
符合條件的所有花費
最後我們將 \(a\) 陣列從大到小排序,針對b裡的每一個值 \(bi\),我們在a數組裡查詢第一個大於\(m- bi\)的數的下標,答案加上下標減一的值,為什麼要這麼做?針對有序的\(a\)
我們所查詢的下標減1的值就是\(a\)陣列中與\(bi\)相加\(<=m\)的數的個數,
所以也有這麼多方案是符合題意的。
為什麼要查詢第一個大於\(m-bi\)的數的下標?因為小於其下標的每一個值
加上\(bi\)的值一定小於等於\(m\),但我們不知道a陣列中是
否有\(m-bi\),所以需要查詢第一個大於它的數的下標減一,最後把答
案加起來即可,可能聽起來有點迷糊,直接看程式碼要清晰許多。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #define int long long using namespace std; const int maxn=5e6; inline int read(){ int f=1; int ret=0; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-f; ch=getchar(); } while(ch<='9'&&ch>='0'){ ret=ret*10+(ch^'0'); ch=getchar(); } return ret*f; } int n,m; int cnt1; int cnt2; int a[maxn]; int b[maxn]; int v[maxn]; void dfs1(int id,int mx,int sum){ if(sum>m){ return ; } if(id>mx){ a[++cnt1]=sum; return ; } dfs1(id+1,mx,sum+v[id]); dfs1(id+1,mx,sum); } void dfs2(int id,int sum){ if(sum>m){ return ; } if(id>n){ b[++cnt2]=sum; return ; } dfs2(id+1,sum+v[id]); dfs2(id+1,sum); } signed main(){ cin>>n>>m; for(int i=1;i<=n;i++){ v[i]=read(); } int mid=n>>1; dfs1(1,mid,0); dfs2(mid+1,0); sort(a+1,a+1+cnt1); int ans=0; for(int i=1;i<=cnt2;i++){ ans+=upper_bound(a+1,a+1+cnt1,m-b[i])-a-1; } cout<<ans; return 0; }
原本搜尋的時間複雜度為 \(O(2^n)\)
優化為 \(O(2^\frac{n}{2})\)
但最後需要加上合併的時間複雜度