蓝桥杯刷题——包子凑数(数论加完全背包)

206 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

这道题涉及一个数论,如果不知道这个定理可能做题有一些困难。

题目

image.png

裴蜀定理

首先介绍一个数论,裴蜀定理。如果有两个数字a,b,他们之间的最大公约数为gcd,那么对于任意的整数x,y,ax+by都一定是gcd的倍数,也一定存在整数x,y使得ax+by=gcd。n个数字的推广:若a1,a2,...,an的最大公约数为gcd,则任意x1,x2,....,xn使得a1X1+a2X2+....+anXn是gcd的倍数,也一定存在整数x1,x2,x3...xn,a1X1+a2X2+....+anXn=gcd。

思路

现在我们这样想,如果两个数字a,b公约数gcd不为1,那么他们凑不出的数一定是无限个的,因为任意整数x,y都是gcd的倍数,所以一定是肯定是有无数个数是凑不出来的。而如果gcd为1,那么就一定不会有无数个数字是凑不出来的。但还是会有一些数字无法凑出来的,因为ax+by=1,x和y是有可能为负数的。现在包子有n个,也就是n个数字,我们就可以用上面裴蜀定理的推广来做了。先求出这n个数字的最大公约数,判断是否为1,如果不为1,之间返回INF。如果为1,我们就可以运用完全背包来完成这道题了。

代码

#include <bits/stdc++.h>
using namespace std;
int bz[110]={0};
int dp[100010]={0};
int gcd(int a,int b){
  while(b){
    int c=0;
    c=b;
    b=a%b;
    a=c;
  }
  return a;
}
int main()
{
  int n;
  cin>>n;
  for(int i=0;i<n;i++){
    cin>>bz[i];
  }
  if(n==1&&bz[0]==1)
  {
    cout<<0;
    return 0;
  }else if(n==1)
  {
    cout<<"INF";
    return 0;
  }
  sort(bz,bz+n);
  int cnt=gcd(bz[0],bz[1]);
  if(n>2)
    for(int i=2;i<n;i++){
      cnt=gcd(cnt,bz[i]);//求出最大公约数
    }
  if(cnt!=1)
  {
    cout<<"INF";
    return 0;
  }
  int ans=0;
  dp[0]=1;
  for(int j=0;j<n;j++){
    for(int i=bz[j];i<=10000;i++)
      dp[i]|=dp[i-bz[j]];//这里用按位或运算
  }
  for(int i=1;i<=10000;i++){
    if(!dp[i])
      ans++;
  }
  cout<<ans;
  return 0;
}