此题可以用中间相遇法做,详细可以看这里。
我们将物品分为两半,求出两块中任意组合物品二不超过m的方案存在两个数组中。
用dfs暴力即可。
然后将后数组排序,再对前数组中的res[i]用m减去后剩下的值,在后数组中二分,
返回的即为当前有几种方案,全部加起来即可。
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
ll a[1005];
ll res[3000005];
int n;ll m;
ll nex[3000005];
int pren=n/2;
ll pans=0;
ll ans=0;
int pcnt=0,ncnt=0;
int bSearch(int l,int r,ll x)
{
int pos=-1;
int mid=((l+r)>>1);
while(l<=r)
{
mid=((l+r)>>1);
if (nex[mid]>x)
{
r=mid-1;
}
else
{
l=mid+1;
pos=mid;
}
}
return pos;
}
void predfs(int now,ll V)
{
if(V>m)return;
if(now==pren+1)
{
res[++pcnt]=V;
return;
}
predfs(now+1,V+a[now]);
predfs(now+1,V);
return;
}
void nextdfs(int now,ll V)
{
if(V>m)return;
if(now==n+1)
{
nex[++ncnt]=V;
return;
}
nextdfs(now+1,V+a[now]);
nextdfs(now+1,V);
return;
}
int main()
{
register int i;
scanf("%d %lld",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
pren=n/2;
predfs(1,0);
nextdfs(pren+1,0);
sort(nex+1,nex+ncnt+1);
for (int i=1;i<=pcnt;i++)
{
ans+=bSearch(1,ncnt,m-res[i]);
}
printf("%lld",ans);
return 0;
}