CH2401 送禮物(雙向搜尋)
阿新 • • 發佈:2018-12-10
題目
作為懲罰,GY被遣送去幫助某神牛給女生送禮物(GY:貌似是個好差事)但是在GY看到禮物之後,他就不這麼認為了。某神牛有N個禮物,且異常沉重,但是GY的力氣也異常的大(-_-b),他一次可以搬動重量和在w(w<=2^31-1)以下的任意多個物品。GY希望一次搬掉儘量重的一些物品,請你告訴他在他的力氣範圍內一次效能搬動的最大重量是多少。
題解
雙向搜尋 如果從一個方向搜尋,會不斷的產生分支,搜尋樹呈△,在深層子樹上浪費的時間相當多。 如果可以從兩個方向同時開始搜尋,那麼每次搜尋的任務只有一半,搜尋樹呈◇。 雙向搜尋把兩棵搜尋樹合併在一起,產生兩棵深度減半的搜尋樹,在中間交匯出現答案。因為這樣的可以搜尋覆蓋整個狀態空間,故答案的正確性很有保障。
雙向搜尋需要題目給出明確的“初態”和“終態”,這題完全符合。 第一次搜尋列舉用1~n/2號的物品能產生的重量。第二次列舉用n/2+1~n號物品能產生的重量。在中間交匯的地方,用過尋找前驅的方法,使得搜尋2得到的重量+搜尋1中的狀態 最接近w。
程式碼
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=60; int w,n,ans=0; int a[maxn]; bool cmp(int a1,int a2) { return a2>a1; } int m=0,b[(1<<23)+10]; long long now=0; void dfs_1(int k,int ed)//1~mid { if(now>w) return ; if(k>ed) { b[++m]=now; return ; } dfs_1(k+1,ed); now+=a[k]; dfs_1(k+1,ed); now-=a[k]; } void dfs_2(int k,int ed) { if(now>w) return ; if(k>ed)//找前驅 { if(w-now<b[1]) return ; int tmp=upper_bound(b+1,b+m+1,w-now)-b-1; tmp=now+b[tmp]; if(tmp>ans) ans=tmp; return ; } dfs_2(k+1,ed); now+=a[k]; dfs_2(k+1,ed); now-=a[k]; } int main() { scanf("%d%d",&w,&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1,cmp); dfs_1(1,n/2); sort(b+1,b+m+1); dfs_2(n/2+1,n); printf("%d\n",ans); return 0; }