【動態規劃】 方格取數
阿新 • • 發佈:2019-01-30
題目描述
給定一個N*M的矩陣,記錄左上角為(1,1),右下角為(N,M),現在從(1,1)開始取數,每次只能向下或向右移動一個單位,最終到達(N,M),我們把路徑上所有的數相乘,記為C。使C的結果最大已經不能滿足我們了,現在我們想讓C末尾的零最少。
Ps.11000末尾有3個零,100000100末尾有2個零。
輸入輸出格式
輸入格式:輸入檔案matrix.in的第一行包含兩個正整數N,M表示矩陣大小。
接下來N行每行M個正整數給出整個矩陣。
輸出格式:輸出檔名為matrix.out。包含一個整數表示所求最小值。
輸入輸出樣例
3 3
1 2 3
10 5 100
10 8 9
說明
30%的資料滿足 N
100%的資料滿足 1 < N, M ≤ 1000,所有輸入資料不超過32位整範圍。
分析
20161105學校考試的第二題,比較簡單,得分為100。
這道題是一個很明顯的動態規劃。我們不難得知,最後C末位的0的個數與我們路上取到的數的因數中2與5的個數有關。
我們知道,一個2和一個5相乘會得到一個0,所以,這道題也就變成了:
從(1,1)到(n,m)分別求出一條含2的個數最少與含5的個數最少的路徑
在這兩條路徑中選擇2或5的個數較小的那一條,這條路即是我們要求得的路徑
還有一些需要注意的細節問題:
①方格中每個數因子中2和5的個數需要預處理,我在程式中用了w[i][j][0]和w[i][j][1]兩個陣列來儲存
②遞推前f[i][j]的邊界情況一定要處理好,在程式碼中我會詳細敘述
#include<iostream> #include<cstdio> #define MAXM 1001 #define INF 0X7F7F7F7F using namespace std; int m,n; int a[MAXM][MAXM];//a陣列為輸入數字的陣列 int w[MAXM][MAXM][2];//用w陣列分別存下方格中每個數因子中2和5的個數 int f[MAXM][MAXM][2]; int cal(int i,int x)//計算2和5的個數 { int cnt=0; if(i==2) while(x%2==0) { x/=2; cnt++; } if(i==5) while(x%5==0) { x/=5; cnt++; } return cnt; } int main() { //freopen("matrix.in","r",stdin); //freopen("matrix.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) { scanf("%d",&a[i][j]); w[i][j][0]=cal(2,a[i][j]);//用w[i][j][0]存下2的個數 w[i][j][1]=cal(5,a[i][j]);//用w[i][j][1]存下5的個數 } for(int i=0;i<=m;i++)//邊界的預處理 超出邊界的地方視為無窮大 f[0][i][0]=f[0][i][1]=INF; for(int i=0;i<=n;i++) f[i][0][0]=f[i][0][1]=INF; for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) { if(i==1&&j==1)//遞推前的處理 f[1][1][0]=w[1][1][0],f[1][1][1]=w[1][1][1]; else f[i][j][0]=min(f[i-1][j][0],f[i][j-1][0])+w[i][j][0],//轉移方程 f[i][j][1]=min(f[i-1][j][1],f[i][j-1][1])+w[i][j][1]; } printf("%d",min(f[n][m][0],f[n][m][1]));//打印出較小的一條路徑 return 0; }