题意:有n个a[0]--a[n-1]。定义一种操作:可以使数a[i]变成它的阶乘,例如5通过操作变成120。对于每一个数,可以选它,或者选它的阶乘,或者不选。要求通过 至多k次操作,使得被选的数的和为s。求方案总个数。
思路:n不超过25,时间限制是2000ms。首先可以肯定是用dfs暴力搜索,但是n又有些大,单纯暴力必然超时。这时就要用到中途相遇法,并将中间结果存下来。
AC代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <iomanip>
#define debug puts("xxxxxxx")
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf 1<<29
typedef long long ll;
using namespace std;
const int maxn=30;
ll a[maxn],b[maxn],sum,ans;
int k,n;
void init()
{
ans = 0;
b[1] = 1;
for(int i=2; i<19; i++) b[i] = b[i-1]*i;
}
map<int,map<ll,ll> > cnt1;
map<int,map<ll,ll> > cnt2;
map<ll,ll> ::iterator it;
void dfs1(int now,int t,ll val)
{
if(now == n/2+1)
{
cnt1[t][val] ++;
return ;
}
if(t<=k ) dfs1(now+1,t,val);
if(t<=k && val+a[now]<=sum)
{
dfs1(now+1,t,val+a[now]);
}
if(t+1<=k && a[now]<19)
{
if(val+b[a[now]]<=sum) dfs1(now+1,t+1,val+b[a[now]]);
}
return ;
}
void dfs2(int now,int t,ll val)
{
if(now==n+1)
{
cnt2[t][val] ++;
return ;
}
if(t<=k) dfs2(now+1,t,val);
if(t<=k && val+a[now]<=sum) dfs2(now+1,t,val+a[now]);
if(t<k && a[now]<19)
{
if(val+b[a[now]]<=sum) dfs2(now+1,t+1,val+b[a[now]]);
}
return ;
}
int main()
{
init();
cin>>n>>k>>sum;
for(int i=1; i<=n; i++) cin>>a[i];
dfs1(1,0,0ll);
dfs2(n/2+1,0,0ll);
for(int i=0;i<=k;i++)
{
for(int j=0;j<=k;j++)
{
if(i+j>k) break;
for(it = cnt1[i].begin();it!=cnt1[i].end();it++)
{
ans += (it->second) * cnt2[j][sum-(it->first)];
}
}
}
cout<<ans<<endl;
return 0;
}