题目大意
找出从左上走到右下,且经过的数字的乘积末尾的0最少的路。
解题思路
由题意可知,需要尽可能少经过含有因数2和因数5的数字,显然用dp求解。需要一个三维数组dp[N][N][2]
,其中dp[N][N][0]
代表的是经过路径乘2的数量,dp[N][N][1]
代表经过的路径乘5的数量,由于2*5才会出现0,所以只需要让其中一方尽可能少即可。
此外,若矩阵中出现了0,意味着最终的答案最多为1,因为0乘任意数都只会为0,因此,当最终答案比1大时,需要对0进行特判。
至于路径只需要在dp选取前驱时记录即可。
下附代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1005;
int dp[MAXN][MAXN][2];
char road[MAXN][MAXN][2];
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n, a, zx, zy;
cin >> n;
bool zero = false;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> a;
if (!a) {//特判是否有0
zero = true;
zx = i, zy = j;
continue;
}
while (!(a & 1)) {//计算因数2的个数
dp[i][j][0]++;
a >>= 1;
}
while (!(a % 5)) {//计算因数5的个数
dp[i][j][1]++;
a /= 5;
}
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (i == 1 && j == 1)//起点无需计算
continue;
//选取因数2/因数5少的一格作为前驱继承值
//同时按照选取的格子直接记录路径
if (i > 1 && j > 1) {
if (dp[i - 1][j][0] > dp[i][j - 1][0])
road[i][j][0] = 'R', dp[i][j][0] += dp[i][j - 1][0];
else
road[i][j][0] = 'D', dp[i][j][0] += dp[i - 1][j][0];
if (dp[i - 1][j][1] > dp[i][j - 1][1])
road[i][j][1] = 'R', dp[i][j][1] += dp[i][j - 1][1];
else
road[i][j][1] = 'D', dp[i][j][1] += dp[i - 1][j][1];
} else if (i > 1) {
road[i][j][0] = road[i][j][1] = 'D', dp[i][j][0] += dp[i - 1][j][0], dp[i][j][1] += dp[i - 1][j][1];
} else if (j > 1)
road[i][j][0] = road[i][j][1] = 'R', dp[i][j][0] += dp[i][j - 1][0], dp[i][j][1] += dp[i][j - 1][1];
}
}
if (dp[n][n][1] < dp[n][n][0])//选0少的取法
a = 1;
else
a = 0;
if (zero && dp[n][n][a] > 0) {//若最终答案不小于1且矩阵中存在0,则说明可以直接走有0的路径
cout << 1 << endl;
for (int i = 1; i < zx; ++i)
cout << "D";
for (int i = 1; i < zy; ++i)
cout << "R";
for (int i = zx + 1; i <= n; ++i)
cout << "D";
for (int i = zy + 1; i <= n; ++i)
cout << "R";
return 0;
}
cout << dp[n][n][a] << endl;
int x = n, y = n;
string s;
while (x != 1 || y != 1) {//从终点一步步往回走记录倒序的路径
s.push_back(road[x][y][a]);
if (road[x][y][a] == 'R')
--y;
else
--x;
}
for (int i = s.size() - 1; i >= 0; --i)//输出路径
cout << s[i];
}