Anya and Cubes

文章介绍了一种优化过的算法,用于解决在给定整数序列中通过有限次阶乘操作达到目标和的问题,通过分半搜索和剪枝提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给你 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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值