题目描述
给你 n 个数,n≤25。初始序列为 ai, 0≤ai≤10^9。
你有 k 个 !,每个 ! 可以使序列中的一个数变成 ai!。(同一个数至多变一次)
例如 5!=120。
求:选出任意个数使他们和的等于 S 的方案数
输入格式
第一行 n,k,S
第二行 n 个数,分别代表 ai。
输出
一行一个整数,选出数的合法方案数。
首先,我们想到的思路肯定是深搜,就是三种选择,选原数,不选和选阶乘
这种思路的代码放在下面了
注意,只有三十分,想AC的看最下面!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,s;
int a[25+10];
int f[25+10];
int cnt=0;
void dfs(int now,int end,int sum,int used){
if(sum==s){
cnt++;
return;
}
if(now>end)return;
if(sum>s)return;
if(used>k)return;
dfs(now+1,end,sum,used);
dfs(now+1,end,sum+a[now],used);
if(a[now]<=19)dfs(now+1,end,sum+f[a[now]],used+1);
}
signed main(){
scanf("%lld%lld%lld",&n,&k,&s);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
f[1]=1;
for(int i=2;i<=19;i++){
f[i]=f[i-1]*i;
}
dfs(1,n,0,0);
printf("%lld",cnt);
}
但是,这样最大的情况下就要搜索3^25次,是一个特别大的数,肯定要超时
所以,这里就要分开搜
先搜索前一半1 ~(n+1)/2,再搜后面 (n+1)/2+1 ~ n
这里定义两个函数 dfs 和 dfs2,dfs用于找前一半并且记录,dfs2用于求出方案数
先来说dfs
函数外定义一个map数组vis[10+25]
map<int,int>vis[25+10];
vis存的是用了多少个!,和为多少的数有几个
如:用了2个感叹号,和为 8的数有7个
代码为:
vis[2][8]=7;
这里写四个参数,now,end,sum和used
now指的是现在的位置
end指的是结束位置
sum指现在的和
used指 !使用的次数
现在参数定义完了,开始写边界判断
如果sum>s,就是超过了,不满足,剪掉
如果used>k,就是!使用次数超了,不满足,剪掉
如果now>end,就是遍历完了,vis记录一下,结束
然后我们看dfs2函数
函数内容直接复制dfs,只是有一个地方需要改一下
就是在now>end的时候,循环累加cnt(cnt是记录变量)
for(int i=0;i<=k-used;i++){
cnt+=vis[i][s-sum];
}
然后主函数里面还有个小细节,建一个数组f,记录1~19的阶乘,为什么是十九呢,因为大于等于19的数的阶乘已经超出S的数据范围了
最后,调用函数输出cnt就行了
完整代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,s;
int a[25+10];
int f[25+10];
int cnt=0;
map<int,int>vis[25+10];
void dfs(int now,int end,int sum,int used){
if(sum>s)return;
if(used>k)return;
if(now>end){
vis[used][sum]++;
return;
}
dfs(now+1,end,sum,used);
dfs(now+1,end,sum+a[now],used);
if(a[now]<=19)dfs(now+1,end,sum+f[a[now]],used+1);
}
void dfs2(int now,int end,int sum,int used){
if(sum>s)return;
if(used>k)return;
if(now>end){
for(int i=0;i<=k-used;i++){
cnt+=vis[i][s-sum];
}
return;
}
dfs2(now+1,end,sum,used);
dfs2(now+1,end,sum+a[now],used);
if(a[now]<=19)dfs2(now+1,end,sum+f[a[now]],used+1);
}
signed main(){
scanf("%lld%lld%lld",&n,&k,&s);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
f[1]=1;
for(int i=2;i<=19;i++){
f[i]=f[i-1]*i;
}
dfs(1,(n+1)/2,0,0);
dfs2((n+1)/2+1,n,0,0);
printf("%lld",cnt);
}