显然需要 \(dp\) 解决。
第一个思路:用 \(dp_{i}{j}\)代表第 \(i\) 行第 \(j\) 列最小的后缀 \(0\) 数,不幸的是,他被 hack 了。
考虑 \(10\)的形成,贡献只来自于 \(2\) 和 \(5\) 这个因数。
我们容易想到最小化 \(\min\{\sum a_i,\sum b_i\}\)。
ps:\(\sum a_i\) 代表路径中数含有因子 \(2\) 的数量,\(\sum b_i\) 代表路径中数含有因子 \(5\) 的数量。
于是我们就有了两个子任务,最小化 \(\sum a_i\) 和 \(\sum b_i\),然后再取 \(\min\)。
这就是两个简单 \(dp\) 了(我没骗你吧)。
我们记 \(dp1_{i,j}\) 为到坐标为 \((i,j)\) 的点最小的 \(\sum a_i\);
我们记 \(dp2_{i,j}\) 为到坐标为 \((i,j)\) 的点最小的 \(\sum b_i\);
可以得到递推式:
\[dp1_{i,j}=\min\{dp1_{i-1,j},dp1_{i,j-1}\}+a_{i,j}
\]
\[dp2_{i,j}=\min\{dp2_{i-1,j},dp2_{i,j-1}\}+b_{i,j}
\]
对于 \(a_{i,j}\) 和 \(b_{i,j}\) 是可以直接预处理出来的(每次判断是否整除),这个处理的期望复杂度应该是 \(\mathcal O(n^2)\)。
于是我们就能得到一个复杂度为 \(\mathcal O(n^2)\) 的算法。
但是还有个坑:矩阵中的数可能为零,这是 \(2\) 和 \(5\) 也没有贡献。
我们只需判断有没有答案为 \(0\) 的走法(乘积为 \(0\) 时答案为 \(1\)),如果不存在,只需找一条经过 \(0\) 的路线即可。
废话不多说,上代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long
#define inf 2147483647
int n;
int x[1005][1005];
int a[1005][1005],b[1005][1005],dp1[1005][1005],dp2[1005][1005];
int sum1[1005][1005],sum2[1005][1005];
int op1[1005][1005],op2[1005][1005];
int ans[1000005],cnt=0;
int flag=0,xx,yy;
inline int cal(ll n,int a)
{
int ans=0;
while(n%a==0&&n!=0) ans++,n/=a;
return ans;
}
int main()
{
read(n);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) readl(x[i][j]),dp1[i][j]=inf,dp2[i][j]=inf;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(x[i][j]==0) flag=1,xx=i,yy=j;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) a[i][j]=cal(x[i][j],2),b[i][j]=cal(x[i][j],5);
}
dp1[1][1]=a[1][1],sum1[1][1]=b[1][1];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==n&&j==n) continue;
if(i<n)
{
if(dp1[i+1][j]>=dp1[i][j]+a[i+1][j])
{
dp1[i+1][j]=dp1[i][j]+a[i+1][j];
op1[i+1][j]=1;
sum1[i+1][j]=sum1[i][j]+b[i+1][j];
}
}
if(j<n)
{
if(dp1[i][j+1]>=dp1[i][j]+a[i][j+1])
{
dp1[i][j+1]=dp1[i][j]+a[i][j+1];
op1[i][j+1]=2;
sum1[i][j+1]=sum1[i][j]+b[i][j+1];
}
}
}
}
dp2[1][1]=b[1][1],sum2[1][1]=a[1][1];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==n&&j==n) continue;
if(i<n)
{
if(dp2[i+1][j]>=dp2[i][j]+b[i+1][j])
{
dp2[i+1][j]=dp2[i][j]+b[i+1][j];
op2[i+1][j]=1;
sum2[i+1][j]=sum2[i][j]+a[i+1][j];
}
}
if(j<n)
{
if(dp2[i][j+1]>=dp2[i][j]+b[i][j+1])
{
dp2[i][j+1]=dp2[i][j]+b[i][j+1];
op2[i][j+1]=2;
sum2[i][j+1]=sum2[i][j]+b[i][j+1];
}
}
}
}
if(min(dp1[n][n],dp2[n][n])>=1&&flag)
{
printf("1\n");
for(int i=2;i<=yy;i++) putchar(‘R‘);
for(int i=2;i<=n;i++) putchar(‘D‘);
for(int i=yy+1;i<=n;i++) putchar(‘R‘);
}
else if(dp1[n][n]<=dp2[n][n])
{
printf("%d\n",min(dp1[n][n],sum1[n][n]));
int x=n,y=n;
while(x+y!=2)
{
if(op1[x][y]==1) ans[++cnt]=1,x-=1;
else ans[++cnt]=2,y-=1;
}
for(int i=cnt;i>=1;i--)
{
if(ans[i]==1) printf("D");
else printf("R");
}
}
else
{
printf("%d\n",min(dp2[n][n],sum2[n][n]));
int x=n,y=n;
while(x+y!=2)
{
if(op2[x][y]==1) ans[++cnt]=1,x-=1;
else ans[++cnt]=2,y-=1;
}
for(int i=cnt;i>=1;i--)
{
if(ans[i]==1) printf("D");
else printf("R");
}
}
putchar(‘\n‘);
return 0;
}
原文地址:https://www.cnblogs.com/tlx-blog/p/12706147.html