1. 程式人生 > >判斷整除 - 遞推(詳解)

判斷整除 - 遞推(詳解)

1195:判斷整除

【題目描述】

一個給定的正整數序列,在每個數之前都插入+號或-號後計算它們的和。比如序列: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

思路:

設dp[i][j]:=前i個數整除k的餘數是否為j,0為不是,1為是

遞推方程是:dp[i][j]=dp[i-1][(k+j-a[i]%k)%k]||dp[i-1][(j+a[i]%k)%k];

設輸入的數是a,b

我們判斷到第二個數(也就是b的時候),b可以為b也可以為-b,即判斷(a+b)%k和(a-b)%k是否等於j,這要有一個等於那麼就dp[2][j]就等於1

(1)對於(a+b)%k是否為j

若成立則a%k+b%k=j

a%k=j-b%k為了防止陣列小標為負數的情況,把j-b%k變成(j-b%k+k)%k

即a%k=(j-b%k+k)%k

(2)對於(a-b)%k是否等於j

若成立則a%k=(j+b%k)%k

程式碼如下:

#include<iostream>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int INF=0x3f3f3f3f,mod=12345;
const int N=10005,N1=105;
int dp[N][N1],a[N];
//dp[i][j]:=前i個數模k的值等不等於j
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    dp[0][0]=1;//0模任何數餘數都是0
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k-1;j++){
            dp[i][j]=dp[i-1][(k+j-a[i]%k)%k]||dp[i-1][(j+a[i]%k)%k];
        }
    }
    if(dp[n][0])printf("YES\n");
    else printf("NO\n");
}