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

总时间限制: 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;
}
原文地址:https://www.cnblogs.com/zyacmer/p/9887215.html