洛谷1490 买蛋糕(搜索)(剪枝)

题意

选一些最少的数,每个数只能用一次,且能把1~n中的每个数用加法表示出来。

题解

搜索+超级剪枝
先解决第一个问,给你p个数,要做到最大的n,那么构造方法是1,2,4,...,2^p-1,那么可以拼出2^p-1的n。根据这个可以求到第一问。
关键在第二问。假设当前已经拼出1~n,那么还能选的数首先前提是没有用过的,还有一定不能大于n+1(想一想)。
所以如果再加上一个数,最大可以去到n+(n+1)也就是2*n+1。
再抽象一点,在满足1~n时,加入一个合法的数k,那么可以拼出的数扩展到1~n+k。可以发现,在合法的前提下,拼出的数的上限就是使用的数的和。

知道这些后,就可以暴力搜索了。
dfs时记录已选的数的个数k,和sum,以及最大的数mx。根据以上特性,这次合法的数的范围是mx+1~sum+1,枚举它们往后转移。
一个超强剪枝,记最少选m个数,当选了m-1个数时,我们就可以计算出结果了。分两大情况:
1、加多一个数也拼不到n,即2*sum+1<n,此时没有方案贡献。
2、再分两种情况:
   ①  在现在的情况下,选最差的mx+1也可以拼出n,即sum+(mx+1)>=n。那么mx+1~sum+1这些数都可以选,贡献方案(sum+1)-(mx+1)+1=sum-mx+1种;
   ②  至少要选到x才能满足sum+x>=n,可得x>=n-sum,所以只能选n-sum~sum+1这些数,贡献方案(sum+1)-(n-sum)+1=2*sum-n+2种。

剪枝原理

最后一步直接得出答案。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1010;

int n,m;

int cnt=0;
void dfs(int k,int mx,int sum)
{
    if(k==m)//已经选了m-1个
    {
        if(sum*2+1>=n)
            if(sum+mx+1>=n) cnt+=sum-mx+1;
            else cnt+=sum*2+2-n;
        return ;
    }
    for(int i=mx+1;i<=sum+1;i++) dfs(k+1,i,sum+i);
}

int main()
{
    scanf("%d",&n);
    m=1;for(int i=1;i<=n;i<<=1) m++;m--;
    dfs(1,0,0);
    printf("%d %d\n",m,cnt);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值