#E. 钱币组合加强版本+

文章讲述了如何计算使用特定数量和面额的纸币能组合出多少种不同的总额。提供了两种方法,一种是递归算法,通过遍历所有可能的选择来标记每种可能的总额;另一种是利用二进制特性,将问题转换为二进制表示并计算所有可能的状态。这两种方法都用于解决不超过20种纸币且总额不超过10^8的问题。
摘要由CSDN通过智能技术生成

Description

小明手上有 N!!!张纸币(他的钱很奇怪,可以是任意的正整数), 现在他想知道 用这N种纸币可以组合出多少种不同的总额出来。

Format

Input

第一行一个N.

第二行N个正整数,总和不超过10^8

N<=20

Output

如题

Samples

输入数据 1

5

1 1 2 3 4

输出数据 1

11


思路1:递归

用递归来写,每次枚举当前第s个数选或不选最后枚举完n个纸币将纸币的累加结果的vis标记一下,最后枚举1~10000000,求出有多少个vis被标记了即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100001],ans,n;
bool vis[100000001];
void f(int s,int p)//s:目前枚举了多少纸币   p:目前纸币总和
{
  if(s == n + 1)//代表枚举完了n张纸币
  {
       vis[p] = 1;//因为目前纸币总和被凑出来了
       return ;
  }
  for(int i = 0;i < 2;i++) f(s + 1,p + i * a[s]);
}
signed main()
{
  cin>>n;
  for(int i = 1;i <= n;i++) cin>>a[i];
  f(1,0);
  for(int i = 1;i <= 100000000;i++)
    if(vis[i] == 1)
      ans++;
  cout<<ans;
  return 0;
}

思路2:数学方法(二进制)

可以发现,题目中每个钱币可以选也可以不选,所以这不很像二进制吗???

因为总共有n个纸币,所以要有n种状态,n个钱币进行组合的最大数也就是二进制中的{1 1……1}n个1,也就是十进制的2^n-1。

然后我们可以从1~2^n-1进行枚举,然后转化成二进制,最后用个变量m储存二进制中的每一位*a[i]的累加和,再判断vis[m]曾经是否被凑出过,没有就vis[m]=1,ans++,循环结束后输出ans即可。

#include<bits/stdc++.h>
using namespace std;
int n,pw,a[100000001],er[150000001],ans;
bool vis[150000001];
int main()
{
  cin>>n;
  for(int i = 1;i <= n;i++) cin>>a[i];
  pw = (1<<n) - 1;//计算2^n-1
  for(int i = 1;i <= pw;i++)
  {
      int s = 0,t = i;
      while(t)
      {
        s++;//s是该二进制的位数
        er[s] = t % 2;//er储存t的二进制
        t /= 2;
    }//在这里没有翻转er数组的原因是等下从后往前枚举就好了
    int m = 0;
    for(int i = s;i > 0;i--) m += er[i] * a[i];//计算m
    if(vis[m] == 0)
    {
      vis[m] = 1; 
      ans++;
    }
  }
  cout<<ans;
  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值