Codeforces Round #297 (Div. 2) E. Anya and Cubes (双向DFS)

首先思考最暴力的方法: 我们用DFS直接搜索所有可能解, 那么对于每一层,有3种决策: 不选这个数, 选择这个数, 选择这个数的阶乘。  递归深度最大25, 时间复杂度O(3^25), 太大了, 要想办法降低时间复杂度。   还记得之前的简化版吗?  我们在四个集合中每个集合选择一个数字相加,问是否等于一个数S, 我们的方法是预处理两个集合中所有的情况,然后二分。  该题也可以采取相同的策略: 进行两次dfs, 每次递归深度n/2,这样就成功将复杂度降低到O((3^13)*log(3^13)) 。 这是理论上界, 事实上这里面有很多重复的,时间复杂度并没有那么高。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 1000000000;
const int maxn = 35;
const int max_cnt = 2000000+5;
ll cal[maxn], a[maxn], n, k, S, ans;
void init() {
    cal[0] = 1;
    for(int i = 1; i <= 20; i++) {
        cal[i] = cal[i-1] * i;
    }
}
struct node {
    ll sum, k;
    node(ll ss=0, ll kk=0):sum(ss), k(kk) {}
    bool operator < (const node& rhs) const {
        return sum < rhs.sum || (sum == rhs.sum && k < rhs.k);
    }
}c[max_cnt],b[max_cnt];
map<node, int> p;
map<node, int> :: iterator it;
void dfs1(int d, ll sum, int _k) {
    if(_k > k) return ; //第一遍DFS, 求前n/2的组合情况
    if(d > n/2) {
        p[node(sum, _k)]++; return ;
    }
    dfs1(d+1, sum, _k);
    if(sum+a[d] <= S) dfs1(d+1, sum+a[d], _k);
    if(a[d] <= 20 && sum+cal[a[d]] <= S) dfs1(d+1, sum+cal[a[d]], _k+1);
}
void dfs2(int d, ll sum, int _k) {
    if(_k > k) return ; //第二次DFS
    if(d > n) {
        it = p.lower_bound(node(S-sum,0)); //找到第一个大于等于答案的位置
        for( ; it != p.end(); ++it) { //累加答案
            if(it->first.sum == S-sum && _k+it->first.k <= k) ans += it->second;
            if(it->first.sum != S-sum) break;
        }
        return ;
    }
    dfs2(d+1, sum, _k);
    if(sum+a[d] <= S) dfs2(d+1, sum+a[d], _k);
    if(a[d] <= 20 && sum+cal[a[d]] <= S) dfs2(d+1, sum+cal[a[d]], _k+1);
}
int main() {
    init();
    while(~scanf("%I64d%I64d%I64d",&n,&k,&S)) {
        for(int i=1;i<=n;i++) {
            scanf("%I64d",&a[i]);
        }
        p.clear();
        ans = 0;
        dfs1(1,0,0);
        dfs2(n/2+1,0,0);
        printf("%I64d\n",ans);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值