【题解】 [Codeforces-2B] The least round way
题目描述
题目大意
给定一个 \(n \times n\) 的矩阵,求一条从 \((1, 1)\) 到 \((n, n)\) 的路径,使得路径上数的乘积末尾的0最少。
其中,\(2 \le n \le 1000\)。
思路
该题主要考察:DP。
首先,分析阶段性:显而易见,以走到的位置为阶段。
然后,定义状态:\(dp_{i, j}\) 表示从 \((1, 1)\) 到 \((i, j)\) 的所有可能路径中,路径上数的乘积末尾的0最少的个数。
但是,状态难以转移。尝试将0分解为2和5。
于是,\(dp_{i, j, k}\) 表示从 \((1, 1)\) 到 \((i, j)\) 的所有可能路径中,路径上数的乘积质因子k最少的个数。
其中,\(k = {2, 5}\)。
那么从 \((1, 1)\) 到 \((i, j)\) 的所有可能路径中,路径上数的乘积末尾的0最少的个数为 \(min(dp_{i, j, 2}, dp_{i, j, 5})\)。
紧接着,分析状态转移方程:\((i, j)\) 由 上方 \((i - 1, j)\) 和 左方 \((i, j - 1)\) 转移而来。
\[dp_{i, j, 2} = min(dp_{i - 1, j, 2}, dp_{i, j - 1, 2}) + cnt2_{i, j}
\]
\[dp_{i, j, 5} = min(dp_{i - 1, j, 5}, dp_{i, j - 1, 5}) + cnt5_{i, j}
\]
其中, \(cnt2_{i, j}\) 和 \(cnt5_{i, j}\) 分别表示 \((i, j)\) 上的数质因子2和5的个数。
最后,考虑初始化:\(dp_{1, 1, 2} = cnt2_{i, j}, dp_{1, 1, 5} = cnt5_{i, j}\)。
但此题并不仅此,还有一种特殊情况:矩阵中有0。
只需要特判即可,如果DP的结果大于1且矩阵中有0,则结果为0。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned int UINT;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 1005;
int n, a[N][N];
int dp2[N][N], p2[N][N]; // p2 记录 质因子2最少的路径
int dp5[N][N], p5[N][N]; // p5 记录 质因子2最少的路径
int Get2(int x) // 计算一个数质因子2的个数
{
if (!x) return 0;
int ret = 0;
for ( ; x % 2 == 0; x /= 2)
ret ++ ;
return ret;
}
int Get5(int x) // 计算一个数质因子5的个数
{
if (!x) return 0;
int ret = 0;
for ( ; x % 5 == 0; x /= 5)
ret ++ ;
return ret;
}
void Print2(int i, int j) // 输出质因子2最少的路径
{
if (i == 1 && j == 1) return ;
if (p2[i][j] == 1)
{
Print2(i - 1, j);
printf("D");
}
else
{
Print2(i, j - 1);
printf("R");
}
}
void Print5(int i, int j) // 输出质因子5最少的路径
{
if (i == 1 && j == 1) return ;
if (p5[i][j] == 1)
{
Print5(i - 1, j);
printf("D");
}
else
{
Print5(i, j - 1);
printf("R");
}
}
signed main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
scanf("%d", &a[i][j]);
// 初始化
memset(dp2, 0x3f, sizeof dp2), memset(dp5, 0x3f, sizeof dp5);
dp2[1][1] = Get2(a[1][1]), dp5[1][1] = Get5(a[1][1]);
bool flag = false; // flag 记录 是否有0的出现
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
if (a[i][j] == 0) flag = true; // 记录0的出现
if (i > 1) // 由上方转移而来
{
if (dp2[i - 1][j] + Get2(a[i][j]) < dp2[i][j])
{
dp2[i][j] = dp2[i - 1][j] + Get2(a[i][j]);
p2[i][j] = 1;
}
if (dp5[i - 1][j] + Get5(a[i][j]) < dp5[i][j])
{
dp5[i][j] = dp5[i - 1][j] + Get5(a[i][j]);
p5[i][j] = 1;
}
}
if (j > 1) // 由左方转移而来
{
if (dp2[i][j - 1] + Get2(a[i][j]) < dp2[i][j])
{
dp2[i][j] = dp2[i][j - 1] + Get2(a[i][j]);
p2[i][j] = 2;
}
if (dp5[i][j - 1] + Get5(a[i][j]) < dp5[i][j])
{
dp5[i][j] = dp5[i][j - 1] + Get5(a[i][j]);
p5[i][j] = 2;
}
}
}
int res = min(dp2[n][n], dp5[n][n]);
if (flag && res > 1) // 特判特殊情况
{
puts("1");
int x, y;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (a[i][j] == 0)
{
x = i, y = j;
break;
}
for (int i = 1; i < x; i ++ ) printf("D");
for (int i = 1; i < y; i ++ ) printf("R");
for (int i = x; i < n; i ++ ) printf("D");
for (int i = y; i < n; i ++ ) printf("R");
}
else
{
printf("%d\n", res);
// 由最终结果取决路径。若质因子2的个数少于质因子5的个数,则输出质因子2最少的路径;反之亦然。
if (dp2[n][n] < dp5[n][n]) Print2(n, n);
else Print5(n, n);
}
return 0;
}

浙公网安备 33010602011771号