1. 程式人生 > >判斷整除(動態規劃,遞推)

判斷整除(動態規劃,遞推)

tex 同余定理 無限 tails 學習 一個 輸入 有一個 ora

總時間限制: 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
//
特別感謝//https://blog.csdn.net/keyword_/article/details/75303893?utm_source=blogxgwz5 #include<bits/stdc++.h> #define ll long long using namespace std; ll f[10001][1001],a[10001]; /* 把數組定義在int main()外面,初始值為0 數組a用來待會存入輸入數據 數組f是二元數組,從上往下代表一個個處理a[1],a[2]... 從左到右是和求余(取模)有關,下幾行再講 */ int main() { ///輸入環節 ll n,k; cin>>n>>k;
for(ll i=1;i<=n;i++) cin>>a[i]; ///處理環節 f[1][a[1]%k]=1; /* 首先f[1][...]的1表示正在處理a[1]這個數, 然後這裏用的是標記法:即符合某個條件就標記為1, 所以那個1無計算意義,相當於true,不要糾結它, 然後a[1]%k就是a[1]的模啦, 所以f數組的意義也就明了了. 之所以要定為二元數組而不是一元數組, 就是為了從左到右找到一個橫軸下標為a[i]的模, 然後標記為1, 那為什麽f數組創建時橫軸下標上限為1001而不是無限呢? 因為k有範圍,而橫軸是存模的, 一個數%k的取值範圍在0到k-1之間,不可能超過k
*/ for(ll i=2;i<=n;i++)//縱軸從2到n,因為第一個的模已經標記了,所以從2開始 for(ll j=0;j<k;j++)//橫軸j從0到k-1,因為模不可能超過k //兩個for循環遍歷f數組,即考慮全部可能 /* 如果f[i][j]=1,意義則是前i個數總和再去%k的值為j 記住這個1意義只是標記而已 j是模,如果j被標記為1則說明這個模是成立的,是對的 */ if(f[i-1][j]) { /* 如果前i-1個數總和再%k是j的話 (j-a[i])%k是前i個數的模 (j+a[i])%k也是前i個數的模,統統標記為1 這裏分類考慮是因為題目說明了正負情況都要考慮 {如果不能理解(a[1]+a[2]+...+a[i])%k==((a[1]+a[2]+...+a[i-1])%k+a[i])%k 請學習(同余定理)} */ f[i][((j-a[i])%k+k)%k]=1; f[i][((j+a[i])%k+k)%k]=1; /* 那為什麽代碼寫那麽復雜,((j-a[i])%k+k)%k,要%那麽多次??? 那是因為j-a[i]可能%k是負數,但是這個數+k肯定大於0 然後再%k就是正數了 假如不這麽做,並且j-a[i]是負數 他若是直接%k得到的也是個負模,那數組下標j就撐不住負數了 */ } ///輸出環節 if(f[n][0]) cout<<"yes"<<endl;//0號位為1即模為0 else cout<<"no"<<endl; return 0; }

判斷整除(動態規劃,遞推)