题意:
给一个n2的矩阵a,矩阵中的每一个数a(i,j)在0到1e9之间,
现在要找一条从(1,1)到(n,n)的路径,使得路径上数的乘积结果末尾的0数量最少
输出最小值以及路径
思路:
总所周知,末尾0的个数只与质因子中2和5的个数有关,为2和5数量中的较小值。
所以把数拆成2和5,分开dp:
d(i,j,0)表示到(i,j)处最少的2的个数
d(i,j,1)表示到(i,j)处最少的5的个数
答案就是d(n,n,0)与d(n,n,1)中的较小值
dp过程中记录路径即可
但是这样还不够,因为矩阵中的数可能为0,如果走有0的位置,那么最后的乘积肯定是0,末尾0的个数就是1。
如果正常dp得出的结果大于1,且矩阵中有0,那么就随便构造一条经过这个0的路径,这样更优。
ps:
不太明白为什么不能把2和5合在一起考虑,wa13。
拆成min(2的个数)和min(5的个数)两次dp才能过。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
int a[maxm][maxm][2];
int d[maxm][maxm][2];
char pre[maxm][maxm][2];
signed main(){
int n;
scanf("%d",&n);
int flag=0;
int posx,posy;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
scanf("%d",&x);
if(!x){
posx=i,posy=j,flag=1;
continue;
}
while(x%2==0){
a[i][j][0]++;
x/=2;
}
while(x%5==0){
a[i][j][1]++;
x/=5;
}
}
}
for(int i=1;i<=n;i++){//初始化
for(int j=1;j<=n;j++){
d[i][j][0]=d[i][j][1]=1e9;
}
}
d[1][1][0]=a[1][1][0];
d[1][1][1]=a[1][1][1];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(j+1<=n){
if(d[i][j][0]+a[i][j+1][0]<d[i][j+1][0]){
d[i][j+1][0]=d[i][j][0]+a[i][j+1][0];
pre[i][j+1][0]='L';
}
if(d[i][j][1]+a[i][j+1][1]<d[i][j+1][1]){
d[i][j+1][1]=d[i][j][1]+a[i][j+1][1];
pre[i][j+1][1]='L';
}
}
if(i+1<=n){
if(d[i][j][0]+a[i+1][j][0]<d[i+1][j][0]){
d[i+1][j][0]=d[i][j][0]+a[i+1][j][0];
pre[i+1][j][0]='U';
}
if(d[i][j][1]+a[i+1][j][1]<d[i+1][j][1]){
d[i+1][j][1]=d[i][j][1]+a[i+1][j][1];
pre[i+1][j][1]='U';
}
}
}
}
if(flag&&d[n][n][0]>1&&d[n][n][1]>1){//有0走0的话,答案为1
string ans;
for(int i=1;i<posx;i++)ans+='D';
for(int i=1;i<posy;i++)ans+='R';
for(int i=1;i<n-posx+1;i++)ans+='D';
for(int i=1;i<n-posy+1;i++)ans+='R';
cout<<1<<endl;
cout<<ans<<endl;
return 0;
}
if(d[n][n][0]<d[n][n][1])flag=0;
else flag=1;
cout<<d[n][n][flag]<<endl;
int x=n,y=n;
string ans;
while(x!=1||y!=1){
if(pre[x][y][flag]=='L')ans+='R',y--;
else ans+='D',x--;
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
return 0;
}