题解:P10484 送礼物
一道很经典的搜索题目。
题意简述
给定
n 个正整数G_i 。请选择一种方案,选择其中若干个G_i 使得他们的和不超过给定的W 。
思路分析
对于这道题,我们可以使用搜索算法。
如果是普通的搜索,我们枚举每一个数选或不选,然后取最大的合法方案,这样的时间复杂度为
我们可以进行简单的优化,依照上面的原始算法,我们跑两趟 DFS,第一次 DFS 求出
对于每个
两次 DFS 的时间复杂度为
这种做法的实质是将一次搜索划分为了两次搜索,因为搜索的搜索量随搜索树的数高而指数级增加,这样进行折半搜索,降低了搜索树的树高。这种经典的搜索方法成为“双向搜索”。建议完成本题后去了解字串变换这道题。
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=(1<<25);
typedef long long ll;
ll a[N],g[47],n,w,cnt,ans,m,p;
ll r(){
ll x=0;char ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}
bool cmp(int a,int b){
return a<b;
}
bool cmp2(int a,int b){
return a>b;
}
ll found(int x){
ll l=1,r=m,mid,ans=0;
while(l<r){
mid=(l+r+1)/2;
if(a[mid]>x)r=mid-1;
else if(a[mid]<x)l=mid,ans=a[mid];
else if(a[mid]==x)return x;
}
return ans;
}
void dfs1(ll idx,ll sum){
if(idx>p){
a[++cnt]=sum;
return;
}
dfs1(idx+1,sum);
if(sum+g[idx]<=w)dfs1(idx+1,sum+g[idx]);
return;
}
void dfs2(ll idx,ll sum){
if(idx>n){
ans=max(ans,sum+found(w-sum));
return;
}
dfs2(idx+1,sum);
if(sum+g[idx]<=w)dfs2(idx+1,sum+g[idx]);
}
int main(){
w=r(),n=r();
for(int i=1;i<=n;i++)g[i]=r();
sort(g+1,g+1+n,cmp2);
p=n/2+2;
dfs1(1,0);
sort(a+1,a+1+cnt,cmp);
m=unique(a+1,a+1+cnt)-(a+1);
dfs2(p+1,0);
printf("%lld",ans);
return 0;
}
如有错误,请指出。