• 判断整除(动态规划,递推)


    总时间限制: 1000ms 内存限制: 65536kB
    描述
    一个给定的正整数序列,在每个数之前都插入+号或-号后计算它们的和。比如序列:1、2、4共有8种可能的序列:
    (+1) + (+2) + (+4) = 7
    (+1) + (+2) + (-4) = -1
    (+1) + (-2) + (+4) = 3
    (+1) + (-2) + (-4) = -5
    (-1) + (+2) + (+4) = 5
    (-1) + (+2) + (-4) = -3
    (-1) + (-2) + (+4) = 1
    (-1) + (-2) + (-4) = -7
    所有结果中至少有一个可被整数k整除,我们则称此正整数序列可被k整除。例如上述序列可以被3、5、7整除,而不能被2、4、6、8……整除。注意:0、-3、-6、-9……都可以认为是3的倍数。
    输入
    输入的第一行包含两个数:N(2 < N < 10000)和k(2 < k< 100),其中N代表一共有N个数,k代表被除数。第二行给出序列中的N个整数,这些整数的取值范围都0到10000之间(可能重复)。
    输出
    如果此正整数序列可被k整除,则输出YES,否则输出NO。(注意:都是大写字母)
    样例输入
    3 2
    1 2 4
    样例输出
    NO
    //特别感谢//https://blog.csdn.net/keyword_/article/details/75303893?utm_source=blogxgwz5
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    ll f[10001][1001],a[10001];
    /*
    把数组定义在int main()外面,初始值为0
    数组a用来待会存入输入数据
    数组f是二元数组,从上往下代表一个个处理a[1],a[2]...
    从左到右是和求余(取模)有关,下几行再讲
    */
    int main()
    {
    ///输入环节
        ll n,k;
        cin>>n>>k;
        for(ll i=1;i<=n;i++)
            cin>>a[i];
    
    ///处理环节
        f[1][a[1]%k]=1;
        /*
        首先f[1][...]的1表示正在处理a[1]这个数,
        然后这里用的是标记法:即符合某个条件就标记为1,
        所以那个1无计算意义,相当于true,不要纠结它,
        然后a[1]%k就是a[1]的模啦,
        所以f数组的意义也就明了了.
        之所以要定为二元数组而不是一元数组,
        就是为了从左到右找到一个横轴下标为a[i]的模,
        然后标记为1,
        那为什么f数组创建时横轴下标上限为1001而不是无限呢?
        因为k有范围,而横轴是存模的,
        一个数%k的取值范围在0到k-1之间,不可能超过k
        */
        for(ll i=2;i<=n;i++)//纵轴从2到n,因为第一个的模已经标记了,所以从2开始
            for(ll j=0;j<k;j++)//横轴j从0到k-1,因为模不可能超过k
                //两个for循环遍历f数组,即考虑全部可能
                /*
                如果f[i][j]=1,意义则是前i个数总和再去%k的值为j
                记住这个1意义只是标记而已
                j是模,如果j被标记为1则说明这个模是成立的,是对的
                */
                if(f[i-1][j])
                {
                    /*
                    如果前i-1个数总和再%k是j的话
                    (j-a[i])%k是前i个数的模
                    (j+a[i])%k也是前i个数的模,统统标记为1
                    这里分类考虑是因为题目说明了正负情况都要考虑
                    {如果不能理解(a[1]+a[2]+...+a[i])%k==((a[1]+a[2]+...+a[i-1])%k+a[i])%k
                    请学习(同余定理)}
                    */
                    f[i][((j-a[i])%k+k)%k]=1;
                    f[i][((j+a[i])%k+k)%k]=1;
                    /*
                    那为什么代码写那么复杂,((j-a[i])%k+k)%k,要%那么多次???
                    那是因为j-a[i]可能%k是负数,但是这个数+k肯定大于0
                    然后再%k就是正数了
                    假如不这么做,并且j-a[i]是负数
                    他若是直接%k得到的也是个负模,那数组下标j就撑不住负数了
                    */
                }
    ///输出环节
        if(f[n][0]) cout<<"yes"<<endl;//0号位为1即模为0
        else cout<<"no"<<endl;
        return 0;
    }
  • 相关阅读:
    ReverseFind的用法 ; 查找字符中最后一个字符
    sprintf_s() 、sprintf()和printf()区别和用法
    CString/string 区别及其转化
    CC++中strcat()函数
    C++中cstring.h和string.h的区别
    vs中CString的用法,以及所需的头文件
    头文件afx.h作用
    sprintf_s函数用法
    C++ format 函数
    C/C++ typedef用法
  • 原文地址:https://www.cnblogs.com/zyacmer/p/9887215.html
Copyright © 2020-2023  润新知