ybt1195 判斷整除
【題目描述】
一個給定的正整數序列,在每個數之前都插入+號或−號后計算它們的和。比如序列: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之間(可能重復)。
【輸出】
如果此正整數序列可被kk整除,則輸出YES,否則輸出NO。(注意:都是大寫字母)
【輸入樣例】
3 2
1 2 4
【輸出樣例】
NO
【題解】
首先,先看題干中的例子,可以看出,組合后的結果的符號與整除於否無關,所以就不用考慮一半的情況。
再者,序列中的數字,能影響答案的,並不是整個數,而是它模k的值罷了,所以對數據進行處理,縮小數據。
for(int i=1;i<=n;i++) {
cin>>tmp;
tmp%=k;//預處理
a[i]=tmp;
}
接下來,就到了簡化問題的時間
由1,2,4來分析,如果從1開始加入序列,那么1可以被1整除
接下來2加入,這樣就有1和3(結果只取絕對值)兩個數可以整除
再接下來是4,這樣子1,3,5,7就都符合整除規則了。
所以我們就想到(題解就說道)
可以從頭開始,一個一個來(讓元素入列)
因為元素越多,組合方式是以指數級爆炸的,又因為如此多的組合的結果有大量重復,所以說就想到(題解說)可以以k為數組的第二維,只要枚舉余數,就可以將復雜度控制到可控范圍。
那么,就可以用bool變量fi,j表示前i個數是否可以被j整除(1可以,0不可以)
如果要轉移到fi,j的狀態,那么就要在前i-1個數已經入列的基礎上再加第i個數,而第i個數又只有正和負兩種情況所以便只考慮是由加第i個數和減第i個數就好了。
要想加ai得到余數為j的情況,那么前i-1個數必須滿足余數可以為j-ai
反之,若減ai得到余數可以為j,那么前i-1個數必須滿足余數可以為j+ai
所以,要想ai入列后,余數為j,那么上述兩個條件滿足一個就好。
列出方程:fi,j=fi-1,j-ai||fi-1,j+ai
(不過為了防止爆數組,要控制j+-ai的數據在k的范圍內,所以要+k然后%k)
邊界條件:f0,0=1(0個元素,結果為零,所以不管k為多少,余數都為零)
代碼打出來是這樣的:
#include<iostream>
#include<cstring>
using namespace std;
int a[10005],n,k,tmp;
bool f[10005][105];
int main() {
cin>>n>>k;
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++) {
cin>>tmp;
tmp%=k;
a[i]=tmp;
}
f[0][0]=1;//邊界
for(int i=1;i<=n;i++) {
for(int j=0;j<k;j++){
f[i][j]=(f[i-1][(j-a[i]+k)%k]||f[i-1][(j+a[i]+k)%k]);//遞推
}
}
if(f[n][0]){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
return 0;
}
Ps:因為本題轉移時調用的數組的第二維較為隨機,所以就不會懶得滾動數組了······
感謝題解