525E Anya and Cubes(暴力,meet-in-the-middle)
Anya loves to fold and stick. Today she decided to do just that.
Anya has n cubes lying in a line and numbered from 1 to n from left to right, with natural numbers written on them. She also has kstickers with exclamation marks. We know that the number of stickers does not exceed the number of cubes.
Anya can stick an exclamation mark on the cube and get the factorial of the number written on the cube. For example, if a cube reads 5, then after the sticking it reads 5!, which equals 120.
You need to help Anya count how many ways there are to choose some of the cubes and stick on some of the chosen cubes at most kexclamation marks so that the sum of the numbers written on the chosen cubes after the sticking becomes equal to S. Anya can stick at most one exclamation mark on each cube. Can you do it?
Two ways are considered the same if they have the same set of chosen cubes and the same set of cubes with exclamation marks.
The first line of the input contains three space-separated integers n, k and S (1 ≤ n ≤ 25, 0 ≤ k ≤ n, 1 ≤ S ≤ 1016) — the number of cubes and the number of stickers that Anya has, and the sum that she needs to get.
The second line contains n positive integers ai (1 ≤ ai ≤ 109) — the numbers, written on the cubes. The cubes in the input are described in the order from left to right, starting from the first one.
Multiple cubes can contain the same numbers.
Output the number of ways to choose some number of cubes and stick exclamation marks on some of them so that the sum of the numbers became equal to the given number S.
2 2 30
4 3
1
2 2 7
4 3
1
3 1 1
1 1 1
6
In the first sample the only way is to choose both cubes and stick an exclamation mark on each of them.
In the second sample the only way is to choose both cubes but don't stick an exclamation mark on any of them.
In the third sample it is possible to choose any of the cubes in three ways, and also we may choose to stick or not to stick the exclamation mark on it. So, the total number of ways is six.
有n个方块,上面写着一些自然数,还有k个感叹号可用。
你可以选任意个方块,然后选一些贴上感叹号使上面的数值变成阶乘,然后把方块上的值加起来会得到一个和。
求和等于S的选择方法数。
思路:
每个方块可以选,不选,选了且变成阶乘这三种状态。
所以总的状态是
题目中提到了meet-in-the-middle的思想
就是把方块排序,然后均分为两半,先穷举前一半,保存其状态,然后穷举后一半,同时看有没有前一半的状态可以和他合并得到解。于是搜索空间被成功地缩小到了
using namespace std;
#define LL long long
const int N = 30;
int n,k,a[N];
LL s,fa[N],ans = 0;
map<LL,int> mp[N];
void init()
{
fa[1] = 1;
for(int i=2; i<20; i++)//SB 的写成了i=1全错了 擦!!!
fa[i] = fa[i-1] * i;
}
void dfs(int cur,int x,LL sum)
{
if(sum > s)
return ;
if(cur == n/2)
{
mp[x][sum]++;
return ;
}
dfs(cur+1,x,sum);
dfs(cur+1,x,sum+a[cur]);
if(a[cur]<19 && x<k)
dfs(cur+1,x+1,sum+fa[a[cur]]);
}
void dfs2(int cur,int x,LL sum)
{
if(sum > s)
return ;
if(cur == n)
{
for(int i=0; i<=k-x; i++)
{
//printf("~ i = %d , cur = %d , x = %d , sum = %I64d , s-sum=%I64d , !!%d\n",i,cur,x,sum,s-sum,mp[i][s-sum]);
if(mp[i].find(s-sum) != mp[i].end())
{
ans += mp[i][s-sum];
}
}
return ;
}
dfs2(cur+1,x,sum);
dfs2(cur+1,x,sum+a[cur]);
if(a[cur]<19 && x<k)
dfs2(cur+1,x+1,sum+fa[a[cur]]);
}
void check()
{
map<LL,int> ::iterator it;
for(int i=0; i<=k; i++)
{
for(it=mp[i].begin(); it!=mp[i].end(); it++)
{
printf("mp[%d][%I64d] = %d, ",i,it->first,it->second);
}
cout << endl;
}
}
int main()
{
//freopen("in.txt","r",stdin);
init();
cin >> n >> k >> s;
for(int i=0; i<n; i++)
cin>>a[i];
dfs(0,0,0);
// check();
dfs2(n/2,0,0);
cout << ans << endl;
}