CF 2B. The least round way【dp】
题目链接
思路
求所经过路径乘积结果为0的最少数目的路径。
假设已知所经过的路径以及每一个格子内的值,那么答案可以表示为\(ans = x_1*x_2*x_3*x_4...*x_n\),对每一个值进行质因子分解,那么答案数字对0的贡献仅有一种情况,即\(2*5=10\)这种情况。所以只要考虑从\((1,1)\)走到\((n,n)\)的路径上质因子\(2\)或\(5\)数量更少的路径。
\(dp[i][j][x]:\)表示从\((1,1)\)走到\((i,j)\)的路径中,\(x\)出现的最少次数。
转移方向只要考虑从左边走过来还是往右边走过来即可。
转移方程:
\(dp[i][j][x]=min(dp[i-1][j][x],dp[i][j-1][x])+cnt[i][j][x]\)
\(cnt[i][j][x]:\)\(a[i][j]\)这个数字由几个\(x\)组成。
这边的x只要统计2和5即可。
特判一种情况,就是某一个格子内的数字为0的情况。这样的话答案为1,当且仅当\(min(dp[n][n][2],dp[n][n][5])>1\)的情况下考虑。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1000 + 10;
#define gcd __gcd
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int num[N][N][6];
int dp[N][N][6];
PII pos;
void print(int x, int y, int t) {
if(x == 1 && y == 1) return;
if(x > 1 && dp[x][y][t] == dp[x - 1][y][t] + num[x][y][t]) {
print(x - 1, y, t);
printf("D");
}
else {
print(x, y - 1, t);
printf("R");
}
}
void solve() {
int n;
scanf("%d", &n);
bool flag = false;
for(int i = 2; i <= n; i++) {
dp[0][i][2] = dp[0][i][5] = inf;
dp[i][0][2] = dp[i][0][5] = inf;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int x;
scanf("%d", &x);
if(x == 0) {
pos = {i, j};
flag = true;
continue;
}
while(x % 2 == 0) {
num[i][j][2]++;
x /= 2;
}
while(x % 5 == 0) {
num[i][j][5]++;
x /= 5;
}
dp[i][j][2] = min(dp[i - 1][j][2], dp[i][j - 1][2]) + num[i][j][2];
dp[i][j][5] = min(dp[i - 1][j][5], dp[i][j - 1][5]) + num[i][j][5];
}
}
if(flag && min(dp[n][n][2], dp[n][n][5]) > 1) {
puts("1");
for(int i = 1; i < pos.first; i++) {
printf("D");
}
for(int i = 1; i < pos.second; i++) {
printf("R");
}
for(int i = 1; i <= n - pos.first; i++) {
printf("D");
}
for(int i = 1; i <= n - pos.second; i++) {
printf("R");
}
return;
}
printf("%d\n", min(dp[n][n][2], dp[n][n][5]));
if(dp[n][n][2] < dp[n][n][5]) print(n, n, 2);
else print(n, n, 5);
}
int main() {
// freopen("in.txt", "r", stdin);
// int t; cin >> t; while(t--)
solve();
return 0;
}