題意:
給一個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;
}