POJ 3531:判断整除

总时间限制: 

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

分析:

与上一题(糖果)相似,这又是一道与余数有关的动态规划。

每个数的加或减状态决定最终的和,题目说明0,-3,-6都是三的倍数,那么结果得到的-x 与 x 就是等价的。分析本题的描述里的几个等式,发现某个数只取正或负的结果 与 该数正负都取得到的结果是一样的(就是说:你看前四个等式的结果与后面四个等式的结果是不是一样的(忽略正负),而这个两对等式的“1”就是只取正或负。)

利用上述说明,我们可以将第一个数固定为正。

说说动规思想:

定义一个布尔型二维数组dp[i][j] (i代表对前i个数进行计算,j代表计算结果的余数)

首先初始化第一个值dp[1][firstnum%k] (k 就是题目上的k)

基于此创建状态方程:

if(dp[i-1][j]) {

         dp[i][(j+nextnum)%k]=true;

         dp[i][abs((j-nextnum)%k)]=true;

}

就是说:如果从第一个数算到第i-1数能算出余数为j的结果话 那么就拿这个结果进一步与第i个数进行加或减,令它为true就表示这种结果存在。

如果最后dp[n][0]这个结果存在,不就说明存在某种方式使得最后余数为零呗,然后输出YES。

词穷了,上代码:

#include<iostream>
#include<math.h>
using namespace std;
bool dp[10005][105];
int num[10005];
int main() {
	int n, k; cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> num[i];
		num[i] %= k;//直接取该数的余数进行操作
	}
	dp[1][num[1]] == true;//初始化
	for(int i=2;i<=n;i++)//从第二个数开始
		for(int j=0;j<k;j++)
			if (dp[i - 1][j]) {//传递性:上一个数及之前的数和的余数为j,就继续与下一个数相加或相减得到新余数
				dp[i][(j + num[i]) % k] = true;
				dp[i][abs((j - num[i]) % k)] = true;
			}
	if (dp[n][0]) cout << "YES";//如果传到最后能组合成余数为零的情况,即成功!
	else cout << "NO";
	return 0;
}

不懂评论区问问呗。

感谢阅读!!!❤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ctrl AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值