10J:判断整除
- 总时间限制:
- 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
1 #include<iostream> 2 using namespace std; 3 int a[10005]; 4 bool f[10005][105]; //前i个数是否模k等于j 5 int n, k; 6 int main(){ 7 cin>>n>>k; 8 int i, j; 9 for(i = 0; i < n; i++) 10 cin>>a[i]; 11 f[0][0] = true; //0模k肯定等于0 12 for(i = 1; i <= n; i++){ 13 for(j = 0; j < k; j++){ 14 f[i][j] = f[i-1][(j-(a[i-1]%k)+k)%k]||f[i-1][(j+(a[i-1]%k))%k]; 15 } 16 } 17 if(f[n][0]) cout<<"YES"<<endl; 18 else cout<<"NO"<<endl; 19 return 0; 20 }
备注:https://www.cnblogs.com/Wild-Donkey/p/12219856.html
https://blog.csdn.net/m0_37579232/article/details/84994449
其实好好想想能想出来的。一般涉及到序列肯定有一维状态是前i个数,发现这样是不能转移的,涉及到整除问题,肯定需要知道余数是多少。就增加一维j来表示前i个数模k是否可以为j,这个状态很神奇的没有后效性。f(i,j)就表示序列的前i个数模k是否可以等于j。
考虑处理到第i个数,前i-1个数组成的结果是sum(sum当然是不唯一的,但sum具体是什么不重要,有任何一种满足 可以等于j 的要求就可以),那么第i个数前是正号,前i个数的和就是sum+a[i],那么(sum+a[i])%k如果==j,就要求sum%k==j-a[i]%k, 为了防止数组下标为负,处理成sum%k==(j-a[i]%k+k)%k。如果第i个数前是负号,前i个数的和就是sum-a[i],同理sum%k==(j+a[i]%k)%k。
当然,像链接1 一样,在输入数据时就意识到,只需要关心a[i]%k是多少就行了,直接存的就是余数,就可以在递推公式种少写一个%。
要注意我的数组是从0开始的,所以a[i]实际上是a[i-1].
动规一定要注意的一点:填数组的时候一定要把边界值隔过去!!比如我有一次WA就是因为i从0开始,这样把前面的初始值就给覆盖了。