F. Zero Remainder Sum(Codeforces Round #677 (Div. 3))
题目来源
https://codeforces.ml/contest/1433/problem/F
题意分析
题目意思的大致可以描述为,给一个n*m的方阵,并且给一个k,每一行我们最多只能取m/2个数字,问在这个方阵之中,在我们取的所有数的和为k的倍数的情况下,这个数和最大是多少?
思路分析
数据范围不大,想到dp。
首先对每一行进行处理,因为每一行都是独立的。我们要处理出余数从0 - k-1的数的和的最大值,所以开一个数组pre。又因为每一行有长度的限制,所以我们就加一维来规范我们取的长度。所有pre是一个三维的数组,pre[i][j][l]表示的是第i行我们取了l个数,此时的数和对k的余数为j的最大值,这个数组我们可以用一个四重的循环进行初始化。分别循环他的行数,每行数字的个数,长度以及余数。循环长度的时候一定要逆着走,这里应该与01背包中的原理是相同的,即保证每一个数只会被选择一次。
那么根据上面的初始化,我们再将上面的数据映射到我们的dp数组之中。因为我们的dp数组表示的是第i行余数为j的最大值,所以要将长度那一维去掉。于是用一个两重循环初始化dp数组,求所有长度下某个余数的最大值。
在dp数组被初始化之后,接下来将行与行之间的关系进行处理。即用一个k*k的循环,处理出在两行中取数字时候的各个余数的最大值。那么这样一直处理到第n行,ans[n][0]即是我们最终的答案。
最后要提的是要注意所有数组的初始化,具体细节请见代码,其目的是为了排除一些不会出现的情况,不让其对最终答案产生影响。
code
1 #include <bits/stdc++.h> 2 3 #define INF 0x3f3f3f3f 4 #define ll long long 5 using namespace std; 6 const int maxn = 105; 7 8 int pre[maxn][maxn][maxn]; 9 int dp[maxn][maxn]; 10 int ans[maxn][maxn]; 11 int a[maxn][maxn]; 12 int n, m, k; 13 14 signed main(){ 15 scanf("%d%d%d", &n, &m, &k); 16 for (int i=1; i<=n; i++){ 17 for (int j=1; j<=m; j++) scanf("%d", &a[i][j]); 18 } 19 memset(pre, -INF, sizeof (pre)); 20 for (int i=1; i<=n; i++) pre[i][0][0] = 0; 21 22 for (int i=1; i<=n; i++){ 23 int ln = m / 2; 24 for (int p=1; p<=m; p++){ 25 for (int l=min(ln, p)-1; l>=0; l--){ 26 for (int j=0; j<k; j++){ 27 int tmp = a[i][p]; 28 pre[i][(j+tmp) % k][l+1] = max(pre[i][(j+tmp) % k][l+1], pre[i][j][l] + a[i][p]); 29 // cout << i << " " << (j + tmp) % k << " " << l + 1 << " " << pre[i][(j+tmp) % k][l+1] << endl; 30 } 31 } 32 } 33 } 34 memset(dp, -INF, sizeof (dp)); 35 for (int i=1; i<=n; i++){ 36 for (int j=0; j<k; j++){ 37 for (int l=0; l<=m/2; l++){ 38 dp[i][j] = max(dp[i][j], pre[i][j][l]); 39 } 40 } 41 } 42 43 // for (int i=0; i<k; i++){ 44 // for (int j=0; j<=m/2; j++) printf("%d ", pre[1][i][j]); 45 // printf("\n"); 46 // } 47 // printf("---------------------------\n"); 48 // for (int i=1; i<=n; i++){ 49 // for (int j=0; j<k; j++) printf("%d ", dp[i][j]); 50 // printf("\n"); 51 // } 52 // printf("\n"); 53 memset(ans, -INF, sizeof (ans)); 54 for (int i=0; i<k; i++) ans[1][i] = dp[1][i]; 55 56 57 for (int i=2; i<=n; i++){ 58 for (int j=0; j<k; j++){ 59 for (int p=0; p<k; p++){ 60 int tmp = (j + p) % k; 61 ans[i][tmp] = max(ans[i][tmp], ans[i-1][j] + dp[i][p]); 62 } 63 } 64 } 65 66 // for (int i=2; i<=n; i++){ 67 // for (int j=0; j<k; j++) printf("%d ", ans[i][j]); 68 // printf("\n"); 69 // } 70 71 printf("%d\n", ans[n][0]); 72 73 74 return 0; 75 }