CF1433F Zero Remainder Sum

寫在前面

思惟難度不是很大的 DP,代碼實現也很容易。c++

狀態設計模式很套路,轉移也很好理解。git

算法思路

(由於 \(k\) 是經常使用的循環變量,下文中將題面中的模數改成 \(p\)算法

雖然要求的是模 \(p\) 結果爲 \(0\) 的答案,顯然這個結果受到選擇的數的和可能受到模 \(p\) 任何餘數的影響。所以不妨設計狀態的時候想到用一維來表示答案模 \(p\) 的餘數。設計模式

加上這一維以後就是一個揹包模板了。數組

\(f_{i, j, t, k}\) 表示在第 \(i\) 行前 \(j\) 個數中選出 \(t\) 個數,且模 \(p\)\(k\) 的最大答案。spa

那麼顯然有:設計

\[f_{i, j, t, k} = \max\{f_{i, j - 1, t, k}, f_{i, j - 1, t - 1, (k - a_{i, j})\bmod p} + a_{i, j}\} \]

當可能存在多個(或多組) \(a_{i, j}\) 可能將同一個答案更新的時候,取最大值便可。code

這樣分別處理處每一行的答案以後,再設 \(g_{i, j}\) 表示在前 \(i\) 行中選擇某些數使得其和模 \(p\) 的餘數爲 \(j\) 的最大答案。ip

那麼顯然有:get

\[g_{i, j} = \max\{g_{i - 1, (j - k)\bmod p} + f_{i, m, t, k}\} \]

Tips

  • 注意一些邊界條件。

  • 處理的時候可能會出現不合法的狀態,即在某一行內選出某些數,其和模 \(p\) 的餘數根本不可能等於枚舉的 \(k\) 的狀況。這種處理方式歸根到底仍是由於它是從「選 \(0\) 個數的時候餘數卻不爲 \(0\)」這個不合法狀態轉移來的。有個簡單的處理方式,只須要將初值設爲負無窮,而後將合法的初始狀態(即選擇數的個數和餘數都爲 \(0\))設爲 \(0\) 就行了。

Code

  • 代碼中對於每一行分別處理了這一行的 \(f\) 數組,並緊接着更新 \(g\) 數組,所以將 \(f\) 數組刪掉了一維。

  • 代碼中還使用了滾動數組。

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 75;

namespace Read {
	inline int read() {
		int fh = 1, res = 0; char ch = getchar();
		for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
		for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
		return fh * res;
	}
}
using namespace Read;

int n, m, p;

int a[Maxn][Maxn];

int f[2][Maxn][Maxn];
int g[2][Maxn];

int main() {
	n = read(); m = read(); p = read();
	for(register int i = 1; i <= n; ++i) {
		for(register int j = 1; j <= m; ++j) {
			a[i][j] = read();
		}
	}
	memset(g, -0x3f, sizeof(g));
	g[0][0] = 0;
	for(register int i = 1; i <= n; ++i) {
		memset(f, -0x3f, sizeof(f));
		f[0][0][0] = 0;
		for(register int j = 1; j <= m; ++j) {
			f[j & 1][0][0] = 0;
			for(register int t = 1; t <= min(j, m/2); ++t) {
				for(register int k = 0; k < p; ++k) {
					f[j & 1][t][k] = max(max(f[j & 1][t][k], f[j - 1 & 1][t][k]), f[j - 1 & 1][t - 1][(k - (a[i][j] % p) + p) % p] + a[i][j]);
				}
			}
		}
		for(register int j = 0; j < p; ++j) {
			g[i & 1][j] = g[i - 1 & 1][j];
			for(register int t = 0; t <= (m / 2); ++t) {
				for(register int k = 0; k < p; ++k) {
					g[i & 1][j] = max(g[i & 1][j], g[i - 1 & 1][(j - (f[m & 1][t][k] % p) + p) % p] + f[m & 1][t][k]);
				}
			}
		}
	}
	printf("%d", g[n & 1][0]);
	return 0;
}
相關文章
相關標籤/搜索