1. 程式人生 > >【動態規劃】 方格取數

【動態規劃】 方格取數

題目描述

給定一個N*M的矩陣,記錄左上角為(1,1),右下角為(N,M),現在從(1,1)開始取數,每次只能向下或向右移動一個單位,最終到達(N,M),我們把路徑上所有的數相乘,記為C。使C的結果最大已經不能滿足我們了,現在我們想讓C末尾的零最少

Ps.11000末尾有3個零,100000100末尾有2個零。


輸入輸出格式

輸入格式:

輸入檔案matrix.in的第一行包含兩個正整數NM表示矩陣大小。

接下來N行每行M個正整數給出整個矩陣。

輸出格式:

輸出檔名為matrix.out。包含一個整數表示所求最小值。

輸入輸出樣例

3 3

1 2 3

10 5 100

10 8 9

說明

30%的資料滿足 N

, M ≤ 5;

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;
}