判斷整除 - 遞推(詳解)
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"); }