codeforces 2B 解题报告

本文介绍了一种通过动态规划寻找矩阵中从左上角到右下角路径的算法,目标是最小化路径上数字乘积末尾0的数量。文章详细阐述了算法原理,并提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

题目大意:给定规定大小的方阵。要求从方阵的左上方出发到达右下方。每次只能向右边或者向下方移动。求得所有路过数的连续乘法的积, 使得结果的末尾有最少的0。

题目分析:
1. 使得最终结尾的0最少,等价于结果中2和5成对的数量最少。对因子2和因子5进行两次dp, 求两次结果的最小值即可;
2.如果存在矩阵元素0, 如果最终结果0的数量大于等于1,那么结果就不会比存在0的路径最优。预处理时可以把0当做10;
3.假设二维dp, 形如dp(i, j), 表示在i, j位置结果拥有最少的因子2或因子5.
使得最终结果有最少的2和最少的5, 递推公式为:

dp[ i ][ j ] = min( dp[ i - 1][ j ], dp[ i ][ j - 1] ) + two(five)[ i ][ j ];

解题代码:

/*************************************************************************
    > File Name: main.cpp
    > Author: gxg
    > Method: dp + 路径还原 
    > Created Time: 2017年02月13日 星期一 15时20分31秒
 ************************************************************************/

#include <bits/stdc++.h>
using namespace std;

const int Max = 1111;
int n, tmp;               // n 是矩阵大小, tmp记录输入的矩阵元素
int two[Max][Max], five[Max][Max], path[Max][Max][2];   //two[][]记录矩阵元素预处理的因子2的个数
                                                        //five[][]记录矩阵元素预处理的因子5的个数
                                                        //记录两次dp的路径
int x, y; //记录零元素的坐标

int main()
{
    scanf("%d", &n);
    int zero = 0;      //标记矩阵元素是否存在零
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= n; ++ j)
        {
            scanf("%d", &tmp);
            if(tmp == 0)
            {
                zero++;
                tmp = 10;         //将0当做10处理
                x = i, y = j;
            }

            //预处理
            int cnt = 0;
            while(tmp % 2 == 0) tmp /= 2, cnt++; two[i][j] = cnt;
            cnt = 0;
            while(tmp % 5 == 0) tmp /= 5, cnt++; five[i][j] = cnt;
        }
    }

    //边界处理
    for (int i = 1; i <= n; ++ i) two[i][1] += two[i-1][1], five[i][1] += five[i-1][1];
    for (int i = 1; i <= n; ++ i) five[1][i] += five[1][i-1], two[1][i] += two[1][i-1];
    for (int i = 1; i <= n; ++ i) path[i][1][0] = path[i][1][1] = 0, path[1][i][0] = path[1][i][1] = 1; //0表示向下, 1表示向右

    //dp过程
    for (int i = 2; i <= n; ++ i)
    {
        for (int j = 2; j <= n; ++ j)
        {
            //two[i][j] += min(two[i-1][j], two[i][j-1]);
            if(two[i-1][j] < two[i][j-1]) two[i][j] += two[i-1][j], path[i][j][0] = 0;
            else two[i][j] += two[i][j-1], path[i][j][0] = 1;
            //five[i][j] += min(five[i-1][j], five[i][j-1]);
            if(five[i-1][j] < five[i][j-1]) five[i][j] += five[i-1][j], path[i][j][1] = 0;
            else five[i][j] += five[i][j-1], path[i][j][1] = 1;
        }
    }

    int result = min(two[n][n], five[n][n]);

    //选择最终路径
    int cho = 0;
    if(two[n][n] >= five[n][n]) cho = 1;

    //零元素存在且必须输出零元素所在路径的情况
    if(zero && result >= 1)
    {
        printf("1\n");
        int x1 = n - x, y1 = n - y;
        x--, y--;
        while(x--) putchar('R');
        while(y--) putchar('D');
        while(x1--) putchar('D');
        while(y1--) putchar('R');
        return 0;
    }


    printf("%d\n", result);
    x = n, y = n;
    vector<char> vec;
    while(x != 1 || y != 1)
    {
        if(path[x][y][cho] == 0) vec.push_back('D'), x--;
        else vec.push_back('R'), y--;
    }

    for (int i = vec.size() - 1; i >= 0; -- i) putchar(vec[i]);
    printf("\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值