DP+预处理+递归输出 CF2B The least round way

来源:
CF2B The least round way

题目描述
There is a square matrix 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 nn ( 2<=n<=1000 ), nn is the size of the matrix. Then follow n lines containing the matrix elements (non-negative integer numbers not exceeding 10^{9} ).

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

大体意思就是在一个矩阵中找出一条路径,使得这条路径上的数相乘后得到的乘积的后导0最少——分两种情况:(1)路径中有0;(2)没有0。

首先分析,如果存在后导0,那么乘数的因子一定含有2与5,min(2,5)就是后导0的个数,所以这里进行预处理,分别求每个数2与5的因子个数。

其次状态转移转移方程:f[i][j][k]=min(f[i-1][j][k],f[i][j-1][k])+num[i][j][k]。(f,表示到达(i,j)时最少因子个数;k代表因子是2或5)

最后输出路径:逆DP过程,先判断到(i,j)点是从上方还是从左方到达的,一直递归到初始点,回溯输出路径。

#include <bits/stdc++.h>
//dfs 大法师
using namespace std;
const int mod=20100403;
const int Inf=0x3f3f3f3f;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,t,num[1002][1002][2],f[1002][1002][2];
void Print(int i,int j,int k) {
    if(i==1&&j==1) {                //递归输出函数
        putchar(k? 'D':'R');
        return ;            //边界
    }
    if(i==1)
        Print(i,j-1,0);
    else if(j==1)
        Print(i-1,j,1);
    else if(f[i][j][t]==f[i][j-1][t]+num[i][j][t])
        Print(i,j-1,0);
    else
        Print(i-1,j,1);
    if(i!=n||j!=n)
        putchar(k? 'D':'R');    //在(n,n)处不输出
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int ans;
    scanf("%d",&n);
    for(int i=1,k;i<=n;++i)
        for(int j=1;j<=n;++j) {        //预处理因子个数
            scanf("%d",&k);
            if(!k) {
                num[i][j][0]=num[i][j][1]=1;
                t=i;    //特判0,记录位置
            }
            else {
                for(;!(k%2);k/=2)
                    ++num[i][j][0];
                for(;!(k%5);k/=5)
                    ++num[i][j][1];
            }
        }
    for(int i=1;i<=n;++i)
        f[0][i][0]=f[i][0][0]=f[0][i][1]=f[i][0][1]=Inf;    //由于转移方程使用min,将边界赋为最大值
    f[1][1][0]=num[1][1][0];
    f[1][1][1]=num[1][1][1];
    for(int k=0;k<2;++k)
        for(int i=1;i<=n;++i)
            for(int j=i==1? 2:1;j<=n;++j)
                f[i][j][k]=min(f[i][j-1][k],f[i-1][j][k])+num[i][j][k];
    ans=min(f[n][n][0],f[n][n][1]);
    if(t&&ans>1) {            //情况2
        printf("1\n");
        for(int i=1;i<t;++i)
            putchar('D');
        for(int i=1;i<n;++i)
            putchar('R');
        for(int i=t;i<n;++i)
            putchar('D');
    }
    else {                    //情况1
        printf("%d\n",ans);
        t=!(f[n][n][0]<f[n][n][1]);
        Print(n,n,1);
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值