呜呜呜被这道题干翻饮恨西北了。
有一个 N × N N\times N N×N 网格。让单元格 ( i , j ) (i,j) (i,j) 表示从上往下第 i i i 行和从左往上第 j j j 列中的单元格。每个单元格包含一个从 "1"到 "9"的数字;单元格 ( i , j ) (i,j) (i,j) 包含 A i , j A_{i,j} Ai,j 。
最初,一个标记位于 ( 1 , 1 ) (1,1) (1,1) 单元。设 S S S 为空字符串。重复以下操作 2 N − 1 2N-1 2N−1 次:
经过 2 N − 1 2N-1 2N−1 次操作后,标记位于 ( N , N ) (N,N) (N,N) 单元格, S S S 的长度为 2 N − 1 2N-1 2N−1 。
将 S S S 解释为整数。分数就是这个整数除以 M M M 的余数。
求可达到的最大得分。
根据位值原理,我们可以假设单元格 ( i , j ) (i, j) (i,j) 上写的是整数 A i , j ⋅ 1 0 2 N − i − j A_{i,j} \cdot 10^{2N - i - j} Ai,j⋅102N−i−j 而不是数字 A i , j A_{i,j} Ai,j,那么得分就变成了路径上每个单元格上所写整数的总和对 M M M 取模的结果。
注意到数据范围内的 N N N 并不大,考虑从左上角向右下角进行搜索呢。
答案是不行,因为路径总长 2 N − 1 2N - 1 2N−1,其中大部分操作有两种选择(向右或向下),总时间复杂度为 O ( 2 2 N − 1 ) O(2^{2N - 1}) O(22N−1),这显然是不可行的。
但是!!我们可以发现这样子开根号之后可行!
2 N − 1 2N - 1 2N−1 最大是 39 39 39,这其实是一个提示点。
当我们发现一个可以进行搜索的问题的时间复杂度中指数为 30 ∼ 40 30 \sim 40 30∼40 时,就应该考虑下能否使用 meet - in - the - middle,因为 O ( 2 N ) O(2^N) O(2N) 的时间复杂度是可以接受的。
考虑能否使用 meet - in - the - middle 求解:我们可以将路径以副对角线为中点分为两部分。
对于副对角线上所有点,记录从左上角出发到该点之前能够组成的值,以及从右下角出发到该点之前能够组成的值。
这样对于某副对角线上的点,即可通过某前半部分的和 + 该点的值 + 某后半部分的和来得到整个路径。
接下来,对于每个副对角线上的点,设该点上的值为 k k k,其某前半部分的和为 x i x_i xi,某后半部分的值为 y j y_j yj,问题转化为求 x i + k + y j x_i + k + y_j xi+k+yj 的最大值。
我们可以枚举 x i x_i xi,此时 x i x_i xi 与 k k k 都是已知的,只需要确定 y j y_j yj 的值即可。
因为需要对 M M M 取模,所以答案 x i + k + y j x_i + k + y_j xi+k+yj 的最大可能值为 M − 1 M - 1 M−1,故我们只需要找到小于 M − x i − k M - x_i - k M−xi−k 的最大 y j y_j yj 即可(若找不到即为 y y y 中最大值)。
这里我一开始想的是三分(学高级算法学傻了),10s 后才发现这可以通过对 y y y 进行排序后二分查找来实现。
这样即可将时间复杂度降为 O ( 2 N log 2 N ) O(2^{N} \log 2^{N}) O(2Nlog2N)。可以通过!
#include
#define int long long
using namespace std;
const int N = 21;
int pow10[2 * N];
int a[N][N];
vector<int> v1[N], v2[N];
void dfs1(int x, int y, int sum, int n, int m) {
if (x + y == n + 1) {
v1[x].push_back(sum);
return;
}
if (x + 1 <= n)
dfs1(x + 1, y, (sum + a[x][y]) % m, n, m);
if (y + 1 <= n)
dfs1(x, y + 1, (sum + a[x][y]) % m, n, m);
}
void dfs2(int x, int y, int sum, int n, int m) {
if (x + y == n + 1) {
v2[x].push_back(sum);
return;
}
if (x - 1 >= 1)
dfs2(x - 1, y, (sum + a[x][y]) % m, n, m);
if (y - 1 >= 1)
dfs2(x, y - 1, (sum + a[x][y]) % m, n, m);
}
signed main() {
int n, m;
cin >> n >> m;
pow10[0] = 1;
for (int i = 1; i <= 2 * n; i++)
pow10[i] = (pow10[i - 1] * 10) % m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
a[i][j] = a[i][j] * pow10[2 * n - i - j] % m;
}
}
dfs1(1, 1, 0, n, m);
dfs2(n, n, 0, n, m);
for (int i = 1; i <= n; i++) {
sort(v1[i].begin(), v1[i].end());
sort(v2[i].begin(), v2[i].end());
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int val : v1[i]) {
int sum = (val + a[i][n - i + 1]) % m;
auto it = lower_bound(v2[i].begin(), v2[i].end(), m - sum);
if (it != v2[i].begin())
ans = max(ans, (sum + * (it - 1)) % m);
ans = max(ans, (sum + v2[i].back()) % m);
}
}
cout << ans << '\n';
return 0;
}