Codeforces #319(Div.2) B. Modulo Sum (動態規劃)
In the first sample test you can choose numbers 2 and 3, the sum of which is divisible by 5.
In the second sample test the single non-empty subsequence of numbers is a single number 5. Number 5 is not divisible by 6, that is, the sought subsequence doesn't exist.
In the third sample test you need to choose two numbers 3
In the fourth sample test you can take the whole subsequence.
題意:兩個數n和m,還有a1-an的n個數,判斷是否存在該數列的一個子序列,使得子序列元素的和可以被m整除。
看了下官方題解:
分兩種情況:n>m 和 n<=m。
如果n>m, 可以判斷輸出一定為“Yes”。求出前 i 個數的和 S1-Sn,有鴿巢原理,可以知道至少有兩個數列和對m取模的結果相等,假設為Sl%m=Sr%m,則可以知道(Sl-Sr)%m==0,於是 [ l+1,r ]就是所求的子序列。
如果n<=m, 用動態規劃解決,O(m^2)。dp[i][r]表示到了第i個數,前面子序列的和對m取模是否能夠等於r,於是狀態轉移方程就是,如果前面一個數,有dp[i-1][r], 則後面一個數可以選擇ai使得dp[i][(r+ai)%m]為1,或者不選ai,使dp[i][r]=1。這樣做的目的是為了盡最大努力使得後面的數能夠得到對m取模為某個數的子序列和。遍歷一遍dp[i][0]就得到本題的答案。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 1010; int dp[maxn][maxn], a[maxn]; int main() { int n, m; while (scanf("%d%d", &n, &m) != EOF) { if (n >= m) { for (int i = 0; i < n; i++) scanf("%d", &m); printf("YES\n"); continue; } for (int i = 0; i < n; i++) scanf("%d", &a[i]); memset(dp, 0, sizeof(dp)); for (int i = 0; i < n; i++) { if (!i) dp[0][(a[i] % m)] = 1; else { dp[i][(a[i] % m)] = 1; for (int j = 0; j < m; j++) { if (dp[i - 1][j]) { dp[i][(j + a[i]) % m] = 1; dp[i][j] = dp[i - 1][j]; } } } } int flag = 0; for (int i = 0; i < n; i++) { if (dp[i][0]) flag = 1; } if (flag) printf("YES\n"); else printf("NO\n"); } }