分析:
每个物品有三种决策,直接暴力枚举O(3^n)会超时,但是我们发现O(3^(n/2))不会超时,于是可以采用中途相遇法。
先暴力枚举一半的决策,把对应的值、使用次数和方案数存到map里,然后再枚举另一半,在map中查找。
复杂度O(3^(n/2)log(3^(n/2)))=O(3^(n/2)n)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll s, a[30], fact[20], ans;
map<ll, int> mp[30];
int n, k;
void dfs1(ll sum, int pos, int cnt)
{
if(pos == n / 2 + 1)
{
if(mp[cnt].count(sum))
mp[cnt][sum]++;
else
mp[cnt][sum] = 1;
return ;
}
dfs1(sum, pos+1, cnt);
if(a[pos] <= s - sum)
dfs1(sum + a[pos], pos+1, cnt);
if(cnt < k && a[pos] <= 19 && fact[a[pos]] <= s - sum)
dfs1(sum+fact[a[pos]], pos+1, cnt+1);
}
void dfs2(ll sum, int pos, int cnt)
{
if(pos == n + 1)
{
for(int i = 0; i + cnt <= k; i++)
if(mp[i].count(s-sum))
ans += mp[i][s-sum];
return ;
}
dfs2(sum, pos+1, cnt);
if(a[pos] <= s - sum)
dfs2(sum + a[pos], pos+1, cnt);
if(cnt < k && a[pos] <= 19 && fact[a[pos]] <= s - sum)
dfs2(sum+fact[a[pos]], pos+1, cnt+1);
}
int main()
{
fact[0] = 1;
for(ll i = 1; i <= 19; i++)
fact[i] = fact[i-1] * i;
scanf("%d%d%I64d", &n, &k, &s);
for(int i = 1; i <= n; i++)
scanf("%I64d", &a[i]);
dfs1(0, 1, 0);
dfs2(0, n/2+1, 0);
printf("%I64d\n", ans);
return 0;
}