ybt 1195:判断整除
OpenJudge NOI 2.6 3531:判断整除
每次添加的数字可能是正数,可能是负数,这样构成一个数字序列。
考虑如下情况:如果最后一个输入的数字是x,前面的数字加和为s
如果以上两种情况中有一种成立,那么就可以得到能被k整除的加和。
而要判断上述条件是否成立,必须要可以做到判断前i个数字的加和整除k后能否得到某数字j。
因此,设计状态定义:
dp[i][j]
为是否存在数字序列方案可以使得前i个数字的加和除k余j,如果存在,值为true;不存在,值为false。
初始状态:dp[0][0]
为前0个数字的加和(加和为0)是否除k余0,是的。因此dp[0][0] = true
。
要求的结果为:前n个数字加和是否可以除k余0,即dp[n][0]
考虑要使前i个数字的加和除k后余j,前i-1个数字的加和必须如何?
设输入的第 i i i个数字为 v v v,前 i − 1 i-1 i−1个数字的加和为 s s s:
推导过程:
由于 ( s + v ) % k = j (s+v)\%k = j (s+v)%k=j,那么一定有 j < k jj<k ,所以 j = j % k = j % k % k j = j\%k=j\%k\%k j=j%k=j%k%k
( s + v ) % k = j ⇒ (s+v)\%k = j \Rightarrow (s+v)%k=j⇒
( s % k + v % k ) % k = j % k % k (s\%k+v\%k)\%k = j\%k\%k (s%k+v%k)%k=j%k%k
两边为 a % k = b % k a\%k=b\%k a%k=b%k的形式,两边被除数加上相同的正整数后,余数一定还是相等的,即 ( a + c ) % k = ( b + c ) % k (a+c)\%k=(b+c)\%k (a+c)%k=(b+c)%k
让两边被除数加上 k − v % k k-v\%k k−v%k
( s % k + v % k + k − v % k ) % k = ( j % k + k − v % k ) % k ⇒ (s\%k+v\%k+k-v\%k)\%k =( j\%k+k-v\%k)\%k \Rightarrow (s%k+v%k+k−v%k)%k=(j%k+k−v%k)%k⇒
( s % k + k ) % k = ( j % k + k − v % k ) % k ⇒ (s\%k+k)\%k =( j\%k+k-v\%k)\%k\Rightarrow (s%k+k)%k=(j%k+k−v%k)%k⇒
s % k = ( j + k − v % k ) % k s\%k =( j+k-v\%k)\%k s%k=(j+k−v%k)%k
推导过程:
由于 ( s − v ) % k = j (s-v)\%k = j (s−v)%k=j,那么一定有 j < k jj<k ,所以 j = j % k = j % k % k j = j\%k=j\%k\%k j=j%k=j%k%k
( s − v ) % k = j ⇒ (s-v)\%k = j \Rightarrow (s−v)%k=j⇒
( s % k − v % k + k ) % k = j % k % k (s\%k-v\%k+k)\%k = j\%k\%k (s%k−v%k+k)%k=j%k%k
两边为 a % k = b % k a\%k=b\%k a%k=b%k的形式,两边被除数加上相同的正整数后,余数一定还是相等的,即 ( a + c ) % k = ( b + c ) % k (a+c)\%k=(b+c)\%k (a+c)%k=(b+c)%k
让两边被除数加上 v % k v\%k v%k
( s % k − v % k + v % k + k ) % k = ( j % k + v % k ) % k ⇒ (s\%k-v\%k+v\%k+k)\%k =( j\%k+v\%k)\%k \Rightarrow (s%k−v%k+v%k+k)%k=(j%k+v%k)%k⇒
s % k % k = ( j % k + v % k ) % k ⇒ s\%k\%k =( j\%k+v\%k)\%k\Rightarrow s%k%k=(j%k+v%k)%k⇒
s % k = ( j + v ) % k s\%k =( j+v)\%k s%k=(j+v)%k
因此,如果前 i − 1 i-1 i−1个数字的加和 s s s满足 s % k = ( j + k − v % k ) % k s\%k = (j+k-v\%k)\%k s%k=(j+k−v%k)%k,那么接下来添加数字 v v v,得到的前 i i i个数的加和满足除k余j。
如果 i − 1 i-1 i−1个数字的加和 s s s满足 s % k = ( j + v ) % k s\%k = (j+v)\%k s%k=(j+v)%k,那么接下来添加数字 − v -v −v,得到的前 i i i个数的加和满足除k余j。
也可以说,只要前 i − 1 i-1 i−1个数字的加和 s s s满足 s % k = ( j + k − v % k ) % k s\%k = (j+k-v\%k)\%k s%k=(j+k−v%k)%k或 s % k = ( j + v ) % k s\%k = (j+v)\%k s%k=(j+v)%k,都可以通过添加v或v的相反数,来让前 i i i个数的加和满足除k余j。
因此可以得出状态转移方程:
dp[i][j] = dp[i-1][j+k-v%k] || dp[i-1][(j+v)%k]
其中v为第i个数字。
或者先将各个数字输入到数组a中,a[i]
为第i个数字,状态转移方程为:
dp[i][j] = dp[i-1][j+k-a[i]%k] || dp[i-1][(j+a[i])%k]
#include
using namespace std;
bool dp[10005][105];//dp[i][j]表示前i个数字(无论正负)的和结果模k能不能得到j
int a[10005];
int main()
{
int n, k, v;
cin >> n >> k;
dp[0][0] = true;
for(int i = 1; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n; ++i)
for(int j = 0; j < k; ++j)//j是除k得到的余数,范围为0~k-1
dp[i][j] = dp[i-1][(k+j-a[i]%k)%k] || dp[i-1][(j+a[i])%k];
cout << (dp[n][0] ? "YES" : "NO");
return 0;
}
#include
using namespace std;
bool dp[10005][105];//dp[i][j]表示前i个数字(无论正负)的和结果模k能不能得到j
int main()
{
int n, k, v;
cin >> n >> k;
dp[0][0] = true;
for(int i = 1; i <= n; ++i)
{
cin >> v;
for(int j = 0; j < k; ++j)//j是除k得到的余数,范围为0~k-1
dp[i][j] = dp[i-1][(k+j-v%k)%k] || dp[i-1][(j+v)%k];
}
cout << (dp[n][0] ? "YES" : "NO");
return 0;
}