Codeforces Round #297 (Div. 2)E. Anya and Cubes 折半搜索

题目连接:

http://codeforces.com/contest/525/problem/E

题意:

n个数,k个魔法棒,s为所求的数,然后让你找有多少种方法,能够使的这n个数之和为s,其中一个魔法棒可以使的一个数变成他的阶乘。

题解:

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

第二种:http://www.cnblogs.com/qscqesze/p/4371851.html
折半搜索
对于每一个数,都有三种策略,拿,不拿,使用魔法棒
那么我们就直接折半搜索然后扔进一个map里面,然后就查询就好啦
13^3*12^3=3 796 416;
复杂度算的刚刚好呀= = (没看懂怎么算的

学到了 折半搜索降低复杂度 这个折半的意思是预先算出一半的结果,再从后一半找到sum-ans1。
还有就是map的运用 按first从小到大默认排序,如果first是结构体, 可以在结构体中写优先级的函数,map就会自动排序啦

代码一:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define mp(x,y) make_pair(x,y)
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//
const int maxn = 1e5+10;
ll n,k,s;
ll a[30],ans;
ll p[30];

struct node{
    ll sum, y;
    node(ll ss=0, ll kk=0):sum(ss), y(kk) {}
    bool operator < (const node& rhs) const {
        if(sum==rhs.sum) return y<rhs.y;
        return sum < rhs.sum;
    }
};

map<node,int> mp;
map<node,int>::iterator it;

void dfs1(int nn,int kk,ll ss){
    if(k < kk) return ;
    if(ss > s) return ;
    if(nn > n/2){
        node t(ss,kk);
        mp[t]++;
        return ;
    }

    dfs1(nn+1,kk,ss);
    dfs1(nn+1,kk,ss+a[nn]);
    if(a[nn] <= 20) dfs1(nn+1,kk+1,ss+p[a[nn]]);
}

void dfs2(int nn,int kk,ll ss){
    if(k < kk) return ;
    if(ss > s) return ;
    if(nn > n){
        it = mp.lower_bound(node(s-ss,0));
        for( ; it!=mp.end(); it++){
            if(it->first.sum==s-ss && k-it->first.y >= kk) ans += it->second;
            if(it->first.sum != s-ss) break;
        }
        return ;
    }

    dfs2(nn+1,kk,ss);
    dfs2(nn+1,kk,ss+a[nn]);
    if(a[nn] <= 20) dfs2(nn+1,kk+1,ss+p[a[nn]]);
}

int main(){
    p[1] = 1;
    for(int i=2; i<=20; i++)
        p[i] = i*p[i-1];

    n = read(), k = read(), s = read();
    for(int i=1; i<=n; i++)
        a[i] = read();

    int mid = n/2;
    dfs1(1,0,0);
    dfs2(mid+1,0,0);

    cout << ans << endl;

    return 0;
}

代码二:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define mp(x,y) make_pair(x,y)
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//
const int maxn = 1e6+10;

struct node{
    ll sum,key;
}bef[maxn];
ll p[30],cubes[30];
ll n,k,s,cnt,ans;
map<ll,ll> mp[30];
map<ll,ll>::iterator it;

void dfs1(ll nn,ll kk,ll ss){
    if(ss > s) return ;
    if(kk > k) return ;
    if(nn > n/2){
        bef[++cnt].sum = ss;
        bef[cnt].key = kk;
        return ;
    }
    dfs1(nn+1,kk,ss);
    dfs1(nn+1,kk,ss+cubes[nn]);
    if(cubes[nn] <= 20) dfs1(nn+1,kk+1,ss+p[cubes[nn]]);
}

void dfs2(ll nn,ll kk,ll ss){
    if(ss > s) return ;
    if(kk > k) return ;
    if(nn > n){
        mp[kk][ss]++;
        return ;
    }
    dfs2(nn+1,kk,ss);
    dfs2(nn+1,kk,ss+cubes[nn]);
    if(cubes[nn] <= 20) dfs2(nn+1,kk+1,ss+p[cubes[nn]]);
}

int main(){
    n = read(), k = read(), s = read();
    for(int i=1; i<=n; i++)
        cubes[i] = read();
    p[1] = 1;
    for(int i=2; i<=20; i++)
        p[i] = i*p[i-1];

    dfs1(1,0,0);
    dfs2(n/2+1,0,0);

    ans = 0;
    for(int i=1; i<=cnt; i++)
        for(int j=0; j<=k-bef[i].key; j++)
            ans += mp[j][s-bef[i].sum];

    cout << ans << endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值