原理链接:http://codeforces.com/contest/525/problem/E
题意:给了n个立方块,上面都有一个数字,然后有k个感叹号,你可以选择放在一个立方块上,之后形成阶乘。问选择一些立方块然后有的贴上感叹号最后和为S的方案数量。
思路:刚开始想dp。。然后每个方块3种决策,然后用记忆化,不过由于S太大,3^25状态可以全部存在,然后就T了。。
后来一看n为25,可以折半搜索(中途相遇),将n分为两半,前一半的所有状态存入map中,一个k对应一个map,然后最后查找的时候必须查找所有加起来小于k的map,
则时间复杂度为O(3^(n/2+1) * logn * k)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define mem(name,value) memset(name,value,sizeof(name))
#define FOR(i,n) for(int i=1;i<=n;i++)
using namespace std;
typedef long long LL;
const int maxn = 30;
const LL inf = 1e16;
int a[maxn],maxv;
LL fac[maxn];
void init(){
maxv = 0;
fac[0] = fac[1] = 1;
for(int i=2;;i++){
fac[i] = i*fac[i-1];
if(fac[i] > inf){
maxv = i-1;
break;
}
}
}
map<LL,LL>ms[maxn];;
int type,up,n,k;
LL ans,S;
void dfs(int cnt,LL state,int cntk){
if(cnt==up){
if(type==1) ms[cntk][state]++;
else{
for(int i=0;i+cntk<=k;i++)
if(ms[i].count(S-state)) ans += ms[i][S-state]; //注意这个地方要判断是否存在,如果直接加的话,会将每一个状态都插入
}
return ;
}
if(a[cnt]<=maxv && cntk <= k && state+fac[a[cnt]] <= S) dfs(cnt+1, state+fac[a[cnt]],cntk+1);
if(state + a[cnt] <=S) dfs(cnt+1, state+a[cnt],cntk);
dfs(cnt+1,state,cntk);
}
int main(){
// freopen("in.txt","r",stdin);
init();
scanf("%d%d%I64d",&n,&k,&S);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
up = n/2+1; type = 1;
dfs(0,0,0);
up = 0; type = 2;
for(int i=n/2+1;i<n;i++) a[up++] = a[i];
ans = 0;
dfs(0,0,0);
printf("%I64d\n",ans);
return 0;
}