题目传送门:http://codeforces.com/contest/2/problem/B
题型:动态规划、数论
题意:
给出n*n的矩阵,从左上角走到右下角,只能向右走或者向下走,将路线上的值全部乘起来,问结果末尾0最少的路线。
分析:
乘积的末尾0,取决于这些数的质因子中的2和5的对数,即决定与最少的2的个数或者是最少的5的个数。若对矩阵中的每个元素进行质因子分解,复杂度为O(n^2*sqrt(n))过大。由于只要2的个数和5的个数,那么只要对每个元素不断除2或者除5即可,复杂度为O(n^2*log(n))可以接受。
采用DP进行路径的寻找,找出含2最少的路径和含5最少的路径,状态转移方程为:
dp(i,j) = min(dp(i-1,j),dp(i,j-1)) + num(i,j);
可以证明末尾0最少的路径是这两条路径中的一条,证明如下:
反证法:假设有一条路径比这两条更优。
所以这条路径的2和5的对数最少,所以相比与其他路径,该路径上必然2最少或者5最少。
然而根据题设,
这条路径含2的个数和含5的个数都不是最少的。
矛盾。
所以2最少的路或者5最少的路最优。 得证。
由于2最少的路径上的末尾0等于2的个数,5最少的路径上的末尾0等于5的个数,所以
ANS = min(dp2(n,n),dp5(n,n));
路径只需逆退即可。
需要注意的是,如果矩阵元素中有0且ANS>1,那么结果为1,路径就是经过该元素所在点的任意路径。
代码:
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int n;
int num2[1010][1010];
int num5[1010][1010];
int dp2[1010][1010];
int dp5[1010][1010];
char str[1234];
int _x,_y;
void init(){
memset(num2,0,sizeof(num2));
memset(num5,0,sizeof(num5));
for(int i=0;i<=n;i++){
dp2[i][0] = dp2[0][i] = 0;
dp5[i][0] = dp5[0][i] = 0;
}
}
void FindRoute2(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==1 && j==1){
dp2[i][j] = num2[i][j];
continue;
}
if(i==1){
dp2[i][j] = dp2[i][j-1] + num2[i][j];
continue;
}
if(j==1){
dp2[i][j] = dp2[i-1][j] + num2[i][j];
continue;
}
dp2[i][j] = min(dp2[i-1][j],dp2[i][j-1]) + num2[i][j];
}
}
}
void FindRoute5(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==1 && j==1){
dp5[i][j] = num5[i][j];
continue;
}
if(i==1){
dp5[i][j] = dp5[i][j-1] + num5[i][j];
continue;
}
if(j==1){
dp5[i][j] = dp5[i-1][j] + num5[i][j];
continue;
}
dp5[i][j] = min(dp5[i-1][j],dp5[i][j-1]) + num5[i][j];
continue;
}
}
}
void Ask2(){
int pre = 0;
int i=n,j=n;
while(i!=1 || j!=1){
if(dp2[i][j-1]<dp2[i-1][j]){
str[pre] = 'R';
j--;
}
else{
str[pre] = 'D';
i--;
}
pre++;
}
for(int i=pre-1;i>=0;i--){
printf("%c",str[i]);
}
puts("");
}
void Ask5(){
int pre = 0;
int i=n,j=n;
while(i!=1 || j!=1){
if(dp5[i][j-1]<dp5[i-1][j]){
str[pre] = 'R';
j--;
}
else{
str[pre] = 'D';
i--;
}
pre++;
}
for(int i=pre-1;i>=0;i--){
printf("%c",str[i]);
}
puts("");
}
void Special(int xx,int yy){
for(int i=2;i<=xx;i++){
printf("D");
}
for(int i=2;i<=n;i++){
printf("R");
}
for(int i=xx+1;i<=n;i++){
printf("D");
}
puts("");
}
int main(){
while(~scanf("%d",&n)){
bool ts = false;
init();
int a,tmp;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&a);
if(a==0){
_x = i;
_y = j;
ts = true;
}
tmp = a;
while(tmp%2==0&&tmp>0){
num2[i][j]++;
tmp/=2;
}
tmp = a;
while(tmp%5==0&&tmp>0){
num5[i][j]++;
tmp/=5;
}
}
}
FindRoute2();
FindRoute5();
int ans = min(dp2[n][n],dp5[n][n]);
if(ts && ans>1){
puts("1");
Special(_x,_y);
continue;
}
for(int i=0;i<=n;i++){
dp2[i][0] = dp2[0][i] = INF;
dp5[i][0] = dp5[0][i] = INF;
}
printf("%d\n",ans);
if(dp2[n][n]<dp5[n][n]) Ask2();
else Ask5();
}
return 0;
}