总时间限制: 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
时间: 2024-10-06 10:42:59