1. 程式人生 > >CF2B The least round way 題解

CF2B The least round way 題解

都是淚呀。。。↑

題目傳送門

題意(直接複製了QWQ)

題目描述

給定由非負整陣列成的\(n \times n\)的正方形矩陣,你需要尋找一條路徑:
以左上角為起點,
每次只能向右或向下走,
以右下角為終點 並且,如果我們把沿路遇到的數進行相乘,積應當是最小“round”,換句話說,應當以最小數目的0的結尾.

輸入格式

第一行包含一個整數 \((2 \leq n \leq 1000)\)\(n\)為矩陣的規模,接下來的\(n\)行包含矩陣的元素(不超過\(10^9\)的非負整數).

輸出格式

第一行應包含最小尾0的個數,第二行打印出相應的路徑(譯註:D為下,R為右)

思路

樓下其實說得蠻清楚了,我主要就是說一下坑。。。
構成末尾是0的只能是\(2^a\)

\(5^b\)相乘,所得的0的個數為\(min(a,b)\),所以,只要2、5分別dp一遍,取一下上與左的最小值就好啦。。。最後求路徑時遞迴求一遍就好啦。。。

TLE的小朋友們看這裡啦。。。

TLE的小朋友們看這裡啦。。。

TLE的小朋友們看這裡啦。。。

(重要的事情說三遍)

此題特別會卡時。
比如說一開始預處理每個數是\(2^a\)\(2^b\)時,需要將此數不間斷地除下去,為什麼呢?因為卡常數。。。也許時我RP的原因吧。。。卡了半天,終於卡過去了。。。
具體詳見程式碼:

程式碼

(我知道你要看這個)

#include<bits/stdc++.h>
using namespace std;//奇醜無比的碼風
int n,a[1010][1010],f[2][1010][1010],dp[2][1010][1010];
int ans,qx,qy;
bool ff;
inline int get2(register int x,register int y){
    if(a[x][y]==0){return 0;} //特判
    register int pt=0;
    while(a[x][y]%2==0) ++pt,a[x][y]/=2; //卡常數
    return pt;
}
inline int get5(register int x,register int y){
    if(a[x][y]==0){return 0;} //特判
    register int pt=0;
    while(a[x][y]%5==0) ++pt,a[x][y]/=5; //卡常數
    return pt;
}
inline void print(register int k,register int x,register int y,register int first){
    if(x==1&&y==1) ;
    else if(x==1) print(k,x,y-1,0);
    else if(y==1) print(k,x-1,y,1);
    else if(dp[k][x][y]==dp[k][x-1][y]+f[k][x][y]) print(k,x-1,y,1);
    else print(k,x,y-1,0);
    if(first==6666) return ;
    putchar(first==0?'R':'D'); //一開始在n,n點時不需要輸出
    return ;
}
int main(){
    while(cin>>n){
        ff=0;qx=0;qy=0;
        for(register int i=1;i<=n;i++){
            for(register int j=1;j<=n;j++){
                cin>>a[i][j];
                if(a[i][j]==0){
                    qx=i;qy=j;
                    ff=1;
                } 
            }
        }
        for(register int i=1;i<=n;i++){
            for(register int j=1;j<=n;j++){
                f[0][i][j]=get2(i,j);
                f[1][i][j]=get5(i,j);
            }
        }
        memset(dp,63,sizeof(dp));
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=n;j++){
                dp[0][i][j]=min(dp[0][i][j],dp[0][i-1][j]);
                dp[0][i][j]=min(dp[0][i][j],dp[0][i][j-1]);//從左格子與上格子中取最小值
                if(i==1&&j==1) dp[0][i][j]=0;
                dp[0][i][j]+=f[0][i][j];
            }
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=n;j++){
                dp[1][i][j]=min(dp[1][i][j],dp[1][i-1][j]);
                dp[1][i][j]=min(dp[1][i][j],dp[1][i][j-1]);//從左格子與上格子中取最小值
                if(i==1&&j==1) dp[1][i][j]=0;
                dp[1][i][j]+=f[1][i][j];
            }
        ans=min(dp[0][n][n],dp[1][n][n]);//初步ans
        if(ans>1&&ff==1){ //特判有0的情況,如果有0,那麼答案只有0或1.
            putchar('1');
            putchar('\n');
            for(register int i=1;i<qx;i++) putchar('D');
            for(register int i=1;i<qy;i++) putchar('R');
            for(register int i=qx;i<n;i++) putchar('D');
            for(register int i=qy;i<n;i++) putchar('R');
            putchar('\n');
        }else{
            cout<<ans;
            putchar('\n');
            if(dp[0][n][n]<dp[1][n][n]) print(0,n,n,6666); //分2、5討論
            else print(1,n,n,6666);
            putchar('\n');
        }
    }
    return 0;
}