【CodeForces】CF2B The least round way

该博客讨论了一种解决寻找正方形矩阵中从左上角到右下角路径的问题,要求路径乘积的末尾零最少。通过动态规划方法计算每个位置的因子2和5的数量,以确定最小尾零数。同时处理矩阵中可能存在的零元素,分析如何构造最佳路径。代码实现中使用了O(n^2)的时间复杂度。
摘要由CSDN通过智能技术生成

题目地址:

https://www.luogu.com.cn/problem/CF2B

题面翻译:
题目描述:
给定由非负整数组成的 n × n n \times n n×n的正方形矩阵,你需要寻找一条路径:
以左上角为起点
每次只能向右或向下走
以右下角为终点
并且,如果我们把沿路遇到的数进行相乘,积应当是最小“round”,换句话说,应当以最小数目的 0 0 0的结尾.

输入格式:
第一行包含一个整数 n n n( 2 ≤ n ≤ 1000 2 \leq n \leq 1000 2n1000), n n n为矩阵的规模,接下来的 n n n行包含矩阵的元素(不超过 1 0 9 10^9 109的非负整数).

输出格式:
第一行应包含最小尾 0 0 0的个数,第二行打印出相应的路径(译注:D为下,R为右)

题目描述:
There is a square matrix n × n n×n n×n , consisting of non-negative integer numbers. You should find such a way on it that

  • starts in the upper left cell of the matrix;
  • each following cell is to the right or down from the current cell;
  • the way ends in the bottom right cell.

Moreover, if we multiply together all the numbers along the way, the result should be the least “round”. In other words, it should end in the least possible number of zeros.

输入格式:
The first line contains an integer number n n n ( 2 < = n < = 1000 2<=n<=1000 2<=n<=1000), n n n is the size of the matrix. Then follow n n n lines containing the matrix elements (non-negative integer numbers not exceeding 1 0 9 10^{9} 109).

输出格式:
In the first line print the least number of trailing zeros. In the second line print the correspondent way itself.

f [ i ] [ j ] [ 0 ] , f [ i ] [ j ] [ 1 ] f[i][j][0],f[i][j][1] f[i][j][0],f[i][j][1]分别是从左上角 ( 1 , 1 ) (1,1) (1,1)走到 ( i , j ) (i,j) (i,j)路径上的非零数的乘积至少有多少个因子 2 2 2和有多少个因子 5 5 5。那么在路径没有 0 0 0存在的情况下,末尾 0 0 0个数最小的乘积的 0 0 0的个数就是 k = min ⁡ { f [ n ] [ n ] [ 0 , 1 ] } k=\min\{f[n][n][0,1]\} k=min{f[n][n][0,1]} f f f是很容易递推的,递推的同时记录一下方向即可。如果矩阵里有 0 0 0,则看一下是否存在乘积末尾不为 0 0 0的路径,如果没有的话,构造一条经过那个 0 0 0的路径即可。

大致思路如上,但本题细节非常多:
1、在递推的时候,优先从非 0 0 0的点走过来,也就是如果有个前驱点是 0 0 0,则直接从另外一个方向转移。这样的话,如果有不含 0 0 0的路径,显然我们这样算能求出正确答案;如果不存在不含 0 0 0的路径,那么任意一个路径都是正确答案。
2、最后求方案的时候,要看一下最终答案是 2 2 2少的路径还是 5 5 5少的路径,看情况找方案。

代码如下:

#include <iostream>

using namespace std;
using PII = pair<int, int>;

const int N = 1010;
int n;
int a[N][N];
int f[N][N][2];
char d[N][N][2], path[N * N];
bool zero;
int xid;

PII calc(int x) {
  PII p = {0, 0};
  if (!x) return p;
  int y = x;
  while (y % 2 == 0) {
    p.first++;
    y /= 2;
  }
  y = x;
  while (y % 5 == 0) {
    p.second++;
    y /= 5;
  }
  return p;
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
      scanf("%d", &a[i][j]);

  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) {
      if (!a[i][j]) {
        zero = true;
        xid = i;
      }

      PII p = calc(a[i][j]);
      f[i][j][0] = p.first, f[i][j][1] = p.second;
      for (int k = 0; k <= 1; k++) {
        if (i > 1 && j > 1) {
          // 优先从非0的方向走过来
          if (!a[i - 1][j]) {
            f[i][j][k] += f[i][j - 1][k];
            d[i][j][k] = 'R';
            continue;
          }
          if (!a[i][j - 1]) {
            f[i][j][k] += f[i - 1][j][k];
            d[i][j][k] = 'D';
            continue;
          }
          if (f[i - 1][j][k] <= f[i][j - 1][k]) {
            f[i][j][k] += f[i - 1][j][k];
            d[i][j][k] = 'D';
          } else {
            f[i][j][k] += f[i][j - 1][k];
            d[i][j][k] = 'R';
          }
        } else if (i > 1) {
          f[i][j][k] += f[i - 1][j][k];
          d[i][j][k] = 'D';
        } else if (j > 1) {
          f[i][j][k] += f[i][j - 1][k];
          d[i][j][k] = 'R';
        }
      }
    }

  int res, k;
  if (f[n][n][0] <= f[n][n][1]) k = 0;
  else k = 1;
  res = f[n][n][k];
  printf("%d\n", zero ? min(1, res) : res);

  // 如果有0,并且不存在乘积非0的路径,则构造一条经过0所在行的路径
  if (zero && res) {
    for (int i = 1; i < xid; i++) putchar('D');
    for (int i = 1; i < n; i++) putchar('R');
    for (int i = xid; i < n; i++) putchar('D');
  } else {
    int idx = 0;
    int x = n, y = n;
    while (x > 1 || y > 1) {
      path[++idx] = d[x][y][k];
      if (d[x][y][k] == 'D') x--;
      else y--;
    }
    for (int i = idx; i; i--) putchar(path[i]);
  }
}

时空复杂度 O ( n 2 ) O(n^2) O(n2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值