F - Path to Integer
F - Path to Integer
Problem Statement
There is an $N\times N$ grid. Let cell $(i,j)$ denote the cell in the $i$-th row from the top and $j$-th column from the left. Each cell contains a digit from 1 to 9; cell $(i,j)$ contains $A_{i,j}$.
Initially, a token is on cell $(1,1)$. Let $S$ be an empty string. Repeat the following operation $2N-1$ times:
- Append to the end of $S$ the digit in the current cell.
- Move the token one cell down or one cell to the right. However, do not move it in the $(2N-1)$-th operation.
After $2N-1$ operations, the token is on cell $(N,N)$ and the length of $S$ is $2N-1$.
Interpret $S$ as an integer. The score is the remainder when this integer is divided by $M$.
Find the maximum achievable score.
Constraints
- $1 \leq N \leq 20$
- $2 \leq M \leq 10^9$
- $1 \leq A_{i,j} \leq 9$
- All input values are integers.
Input
The input is given from Standard Input in the following format:
$N$ $M$
$A_{1,1}$ $A_{1,2}$ $\ldots$ $A_{1,N}$
$A_{2,1}$ $A_{2,2}$ $\ldots$ $A_{2,N}$
$\vdots$
$A_{N,1}$ $A_{N,2}$ $\ldots$ $A_{N,N}$
Output
Print the answer.
Sample Input 1
2 7
1 2
3 1
Sample Output 1
5
There are two ways to move the token:
- Move through $(1,1),(1,2),(2,2)$. Then $S=$
121, and the score is the remainder when $121$ is divided by $7$, which is $2$. - Move through $(1,1),(2,1),(2,2)$. Then $S=$
131, and the score is the remainder when $131$ is divided by $7$, which is $5$.
The maximum score is $5$, so print $5$.
Sample Input 2
3 100000
1 2 3
3 5 8
7 1 2
Sample Output 2
13712
Sample Input 3
5 402
8 1 3 8 9
8 2 4 1 8
4 1 8 5 9
6 2 1 6 7
6 6 7 7 6
Sample Output 3
384
解题思路
经典 Attention Is All You Need。
性质 1:如果经过格子 $(i,j) \, (0 \leq i, j <n)$,那么 $a_{i,j}$ 必然在最终得到的十进制数的第 $2n-2-(i+j)$ 位(从低位往高位数)。因此每个格子上的位置可以进行替换 $a_{i,j} \gets a_{i,j} \times 10^{2n-2-(i+j)} \bmod m$,这样就可以从原本的每个格子拼成十进制数取模,等价变成每个格子求和取模。
性质 2:从 $(0,0)$ 每次只能往右或往下移动到 $(n-1, n-1)$,必定恰好经过反对角线上的一个格子(即 $(0, n-1), (1, n-2), \ldots (i, n-1-i), \ldots, (n-1, 0)$ 中的一个)。否则可以通过反证法证明,如果经过至少两个反对角线上的格子,一定存在某步往左或往上走。
知道这两个性质就好做了,可以根据第二个性质枚举经过的反对角线上的格子。如果经过反对角线格子 $(i,n-1-i)$,此时我们先暴力求出从 $(0,0)$ 走到 $(i, n-1-i)$(含)所有可能的分数(即路径上格子的和),并存到集合 $S$ 中。用同样的方法再求出从 $(i, n-1-i)$(不含)走到 $(n-1, n-1)$ 所有可能的分数并存到集合 $T$ 中。然后枚举 $S$ 中的每个元素 $x$,为了使得取模后分数最大,应该在 $T$ 中找到不超过 $m-1-x$ 的最大值 $y$。如果 $y$ 存在那么对于固定的 $x$ 取模后最大的分数就是 $x+y$;否则 $y$ 不存在,此时 $y$ 应该取 $T$ 中的最大值,取模后最大的分数是 $x+y-m$。
可以通过 dfs 暴力求出从 $(0,0)$ 到 $(i,n-1-i)$ 所有可能的分数,有 $C_{n-1}^{i}$ 种走法(从 $(0,0)$ 走到 $(i,n-1-i)$ 需要 $n-1$ 步,相当于从 $n-1$ 步中选出 $i$ 步用于往右走)。因此对于每个反对角线上的格子,总的方案数是 $\sum\limits_{i=0}^{n-1}{C_{n-1}^{i}} = 2^{n-1}$,对应的 dfs 复杂度为 $O(2^n)$,不会超时。同理从 $(i, n-1-i)$ 走到 $(n-1, n-1)$,等价于从 $(n-1, n-1)$ 每次往左或往右走到反对角线,复杂度也是 $O(2^n)$。
AC 代码如下,时间复杂度为 $O(n 2^n)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 25;
int n, m;
int g[N][N], p[N * 2];
set<int> st[N * N];
int ans;
void dfs1(int x, int y, int s) {
if (x + y == n - 1) {
st[x * n + y].insert(s);
return;
}
if (x + 1 < n) dfs1(x + 1, y, (s + g[x + 1][y]) % m);
if (y + 1 < n) dfs1(x, y + 1, (s + g[x][y + 1]) % m);
}
void dfs2(int x, int y, int s) {
if (x + y == n - 1) {
s = (s - g[x][y] + m) % m;
int t = x * n + y;
auto it = st[t].upper_bound(m - 1 - s);
if (it == st[t].begin()) ans = max(ans, s + *st[t].rbegin() - m);
else ans = max(ans, s + *prev(it));
return;
}
if (x - 1 >= 0) dfs2(x - 1, y, (s + g[x - 1][y]) % m);
if (y - 1 >= 0) dfs2(x, y - 1, (s + g[x][y - 1]) % m);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
p[0] = 1;
for (int i = 1; i < n << 1; i++) {
p[i] = p[i - 1] * 10ll % m;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> g[i][j];
g[i][j] = 1ll * g[i][j] * p[2 * n - 2 - i - j] % m;
}
}
dfs1(0, 0, g[0][0]);
dfs2(n - 1, n - 1, g[n - 1][n - 1]);
cout << ans;
return 0;
}
参考资料
Editorial - Tokio Marine & Nichido Fire Insurance Programming Contest 2025 (AtCoder Beginner Contest 402):https://atcoder.jp/contests/abc402/editorial/12763
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18858487

浙公网安备 33010602011771号