题目大意
给定 n n n 个正整数,从中选出若干个数,使得和不超过 m m m,问有多少种合法的方案。
Solution
乍一眼看一脸不可做,但是看到数据范围就豁然开朗了。
N ≤ 40 N\le40 N≤40
很显然是爆搜。Yes,brute force yyds.
好吧很快发现爆搜会炸,复杂度是 O ( 2 n ) O(2^n) O(2n),是过不了的。
然后我们一拍脑瓜,掏出一个折半搜索,然后就做完了。
先考虑爆搜前一半,复杂度是 O ( 2 20 ) O(2^{20}) O(220),非常可以。
然后总共最多也就 2 20 2^{20} 220 种结果,我们直接掏出一个数组把每个结果存下来。
然后排序。
然后再爆搜后一半,复杂度还是 O ( 2 20 ) O(2^{20}) O(220)。对于每一个结果,我们二分查找一下,就可以得到前一半中有多少个可以与之匹配了。
Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=2e6+10;
ll a[45],lsh[MAXN],tot;
ll m;
int n,fft,lcnt;
void dfs1(int p,ll sum){
if(sum>m) return;
if(p>fft){
lsh[++tot]=sum;
return;
}
dfs1(p+1,sum);
dfs1(p+1,sum+a[p]);
}
ll ans;
void dfs2(int p,ll sum){
if(sum>m) return;
if(p>n){
ans+=upper_bound(lsh+1,lsh+1+tot,m-sum)-lsh-1;
return;
}
dfs2(p+1,sum);
dfs2(p+1,sum+a[p]);
}
int main()
{
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
fft=n/2;
dfs1(1,0);
sort(lsh+1,lsh+1+tot);
dfs2(fft+1,0);
printf("%lld\n",ans);
}
End
校内模拟赛 T2,最水的一题,没有之一。
虽然我很丢脸地挂了 20 pts。