洛谷题单指南-进阶搜索-CF525E Anya and Cubes

原题链接:https://www.luogu.com.cn/problem/CF525E

题意解读:从n个数挑选任意个(>0个),有k次机会可以把任意数变成阶乘,使得挑选出的数之和为s,求总的方案数。

解题思路:

n的数据范围是25,对于每一个数a[i],要么不选,要么选a[i],要么选a[i]!,最坏情况k==n时,每次选择都有三种状态,总的时间复杂度是3^25,显然直接用DFS无法通过。

既然要选出数之和凑出s,不妨使用双向DFS,也就是折半搜索,先对前半部分数据进行DFS,记录下使用了cnt1个阶乘得到总和为sum1的方案数;再对后半部分数据进行DFS,每找到一个独立的方案(使用cnt2个阶乘得到总和sum2),去前半部分找到的方案中去查找,查找的条件是:所有cnt1<=k-cnt2且sum1 = k - sum2的方案数,累加到ans。

这里用来记录前半部分方案数的数据结构为map:

map<int, map<int, int> > mp; //mp[i][j]表示使用了i个阶乘得到j的方案数

在DFS时,需要几个关键变量:

1、u表示当前枚举到第几个数,
2、sum表示当前和,
3、cnt表示当前用了多少个阶乘,
4、maxdepth表示最多枚举到第几个数,
5、choose表示上一个数是否选

100分代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll n, k, s, ans;
int a[30];
ll jc[20]; //jc[i]表示i的阶乘,1~19即可,19!已经超过了s
map<int, map<int, int> > mp; //mp[i][j]表示使用了i个阶乘得到j的方案数

//u表示当前枚举到第几个数,sum表示当前和,cnt表示当前用了多少个阶乘,maxdepth表示最多枚举到第几个数,choose表示上一个数是否选
void dfs1(int u, int maxdepth, ll sum, int cnt, bool choose)
{
    if(choose) //只有上一个数选了,才会影响方案数
    {
        mp[cnt][sum]++; //记录当前和的方案数
        if(sum == s) ans++; //如果当前和等于s,那么就更新答案
    }
    
    if(u > maxdepth) return;
    dfs1(u + 1, maxdepth, sum, cnt, false); //不用当前数
    dfs1(u + 1, maxdepth, sum + a[u], cnt, true); //用当前数,不用阶乘
    if(a[u] < 19 && cnt < k) //剪枝,如果当前数小于19且当前用的阶乘数小于k,那么就可以用阶乘
    {
        dfs1(u + 1, maxdepth, sum + jc[a[u]], cnt + 1, true); //用当前数,用阶乘
    }
}

void dfs2(int u, int maxdepth, ll sum, int cnt, bool choose)
{
    if(choose) //只有上一个数选了,才会影响方案数
    {
        for(int i = 0; i <= k - cnt; i++) //枚举前半部分用了多少个阶乘,最多用k-cnt个阶乘
        {
            if(mp[i].count(s - sum)) ans += mp[i][s - sum]; //如果前半部分用了i个阶乘,并且和为s-sum,那么就更新答案
        }
    }
    
    if(u > maxdepth) return;
    dfs2(u + 1, maxdepth, sum, cnt, false); //不用当前数
    dfs2(u + 1, maxdepth, sum + a[u], cnt, true); //用当前数,不用阶乘
    if(a[u] < 19 && cnt < k) //剪枝,如果当前数小于19且当前用的阶乘数小于k,那么就可以用阶乘
    {
        dfs2(u + 1, maxdepth, sum + jc[a[u]], cnt + 1, true); //用当前数,用阶乘
    }
}

int main()
{
    cin >> n >> k >> s;
    for(int i = 1; i <= n; i++) cin >> a[i];
    mp[0][0] = 1;//初始化,前半部分所有数都不选的方案数为1
    //预处理阶乘
    jc[0] = 1;
    for(int i = 1; i <= 19; i++) jc[i] = jc[i - 1] * i;
    dfs1(1, (n + 1) / 2, 0, 0, false); //枚举前半部分
    dfs2((n + 1) / 2 + 1, n, 0, 0, false); //枚举后半部分
    cout << ans;
    return 0;
}

 

posted @   五月江城  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示