zoukankan      html  css  js  c++  java
  • 倪文迪陪你学蓝桥杯2021寒假每日一题:1.10日(2017省赛A第8题)

    2021年寒假每日一题,2017~2019年的省赛真题。
    本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。

    后面的每日一题,每题发一个新博文,请大家看博客目录https://blog.csdn.net/weixin_43914593
    @

    2017省赛A类第8题,题目链接:
    包子凑数 http://oj.ecustacm.cn/problem.php?id=1322

    1、题目描述


      给出N个整数,每个整数有无限多个。任取一些整数相加,得到大于等于1的数。问有多少个数得不到。如果得不到的有无限多个,输出“INF”;否则输出得不到的数量。
      例如给出4、5,它们组合不能得到的数有6个:1, 2, 3, 6, 7, 11。其他的数都能通过4,5组合得到。


    看下面的题解前请先自己编码求解。

    2、题解

      倪文迪的话:“本题为DP,同时考察了素数、最大公约数的性质。如果给出的n个数的最大公约数不为1,必定恒有数字不被覆盖,这一点可以用来判断结果是否为INF。剩下的问题就是n个数的背包,通过状态转移求解即可。”
      罗勇军老师的话:题目分两步,(1)判断结果是否为INF;(2)如果不是INF,统计数量。考点是“数论gcd+简单DP”。

    2.1 什么时候答案不是INF

      什么时候答案不是INF?也就是说,除了少数一些整数无法组合得到,其他所有的整数都能得到。
      首先看2个数(a、b)的情况,结论是:若(gcd(a, b) = 1),则答案不是INF。

      以两个数4、5为例,任取(x)个4和(y)个5((x,ygeq0)),它们能组合得到的数是:
      (4x+5y=c)
      若把(c)看成常数,这是一个二元一次方程。
      关于二元一次方程(又称为二元线性丢番图方程),请阅读这篇博文:线性丢番图方程
      博文给出了二元一次方程(ax + by = c)有整数解的定理:设(a,b)是整数且(gcd(a, b) = d),如果(d)不能整除(c),那么方程(ax + by = c)没有整数解,如果(d)能整除(c),那么存在无穷多个整数解。
      显然,如果(gcd(a, b) = 1),由于1能整除所有整数,此时(ax + by)能得到所有整数。
      在例子(4x+5y=c)中,因为(gcd(4, 5) = 1),那么不管(c)是什么整数,都存在整数解(x、y),也就是说答案不是INF。
      不过,(x、y)的解可能是负整数,而本题要求(x、y)是非负整数。
      下面证明:当(c)很大时,肯定有(x、y)的非负整数解。
      在博文线性丢番图方程中指出,当(gcd(a, b) = 1)时,(ax + by = c)的通解是:
      (x = x_0 + bn)
      (y = y_0 - an)
      其中(n)是任意整数,而(x_0)(y_0)是一个特解,它显然满足:(ax_0 + by_0 = c).
      两式分别乘以(a、b),得:
      (ax =a x_0 + abn)
      (by = by_0 - abn)
      取(n)是一个正整数,有(ax =a x_0 + abngeq0),即(x)是非负的。而:
      (by=c-ax_0-abn)
      当(c)很大时,(by)也是非负的,即(y)是非负的。
      以上证明了(c)很大时,存在(x、y)的非负整数解。

      以上讨论了给定2个数的情况,若给定多个数(a、b、e、f、)...可以推导出结论:(gcd(a,b,e,f,...)=1)时,答案是非INF。

    2.2 统计

      给定多个数(a、b、e、)...时,计算所有(ax + by +ez+...)的值,最后统计出没有被计算出的整数的数量即可。
      编码很简单。例如(a),把它所有的倍数(i=a、2a、3a、......)都算一遍。(b、e、)...也一样。
      用(dp[i]=1)表示第(i)个整数被计算出来了,最后统计没有被算过的(dp[i])

    3、C++代码

      OJ运行时间4ms

    //newoj User: ln2037
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 13000;
    int a[maxn];
    int dp[maxn]={0};
    
    int main() {
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++)
            cin >> a[i];
        int g = a[1];
        for(int i = 2; i <= n; i++)  //计算所有数的gcd
            g = __gcd(g, a[i]);
        if(g != 1) {
            cout << "INF";
            return 0;
        }
        for(int i = 1; i <= n; i++) {  //dp[i]:第i个整数是否被计算出来
            dp[a[i]]= 1;
            for(int j = 0; j + a[i] < 10000; j++)
                if(dp[j]) {
                    dp[j + a[i]] = 1;
                }
        }
        int ans = 0;
        for(int i = 1; i < 10000; i++)
            if(dp[i] == 0)  
                ans++;
        cout << ans;
        return 0;
    }
    

    4、Java代码

      OJ上运行时间1.4s

    //newoj User: __admin
    import java.util.Scanner;
    
    public class Main{
        public static int gcd(int a, int b) {
            if(b == 0)  return a;
            else return gcd(b, a%b);
        }
    
        public static void main(String args[]) {
            Scanner in = new Scanner(System.in);
            int n = in.nextInt();
            int[] arr = new int[105];
    
            arr[1] = in.nextInt();
            int d = arr[1];
            for(int i = 2; i <= n; i++)        {
                arr[i] = in.nextInt();
                d = gcd(d, arr[i]);
            }
    
            if(d != 1)
                System.out.print("INF");
            else {
                long dp[] = new long[10005];
                dp[0] = 1;
    
                for(int i = 1; i <= n; i++)
                    for(int j = arr[i]; j <= 10000; j++)
                        dp[j] += dp[j-arr[i]];
    
                int res = 0;
                for(int i = 1; i <= 10000; i++)
                    if(dp[i] == 0)
                        res ++;
                System.out.print(res+"
    ");
            }
        }
    }
    

    5、Python代码

      OJ运行时间0.7s。注意看为什么Python代码这么短。

    #new oj User: 20192031003
    def gcd(a,b):
        if b==0:return a
        else:return gcd(b,a%b)
     
    n=int(input())
    numlist=[]
    for i in range(n):
        numlist.append(int(input()))
    gcdnum=numlist[0]
    for i in range(1,n):
        gcdnum=gcd(gcdnum,numlist[i])
    if gcdnum!=1:
        print("INF")
        exit()
    baozinum=[0]*10001
    baozinum[0]=1
    for num in numlist:
        for i in range(0,10001):
            if baozinum[i]==1 and i+num<=10000:
                baozinum[i+num]=1
    print(10001-sum(baozinum))
    
  • 相关阅读:
    【坑】提答题
    Google Code Jam 2014 Round2
    湖北省队互测Week1
    [xyz模拟题]动态维护树的直径
    音乐会的等待【单调栈】
    51nod1202【DP-树状数组维护】
    51nod1113【矩阵快速幂】
    51nod1255【贪心-栈的应用】
    Lightoj1059【最小生成树】
    SPOJ IAPCR2F 【并查集】
  • 原文地址:https://www.cnblogs.com/luoyj/p/14271425.html
Copyright © 2011-2022 走看看