P7074 方格取數

2022-06-20 06:06:08 字數 3939 閱讀 5481

設有 n×m的方格圖,每個方格中都有乙個整數。現有乙隻小熊,想從圖的左上角走到右下角,每一步只能向上、向下或向右走一格,並且不能重複經過已經走過的方格,也不能走出邊界。小熊會取走所有經過的方格中的整數,求它能取到的整數之和的最大值。

第一行有兩個整數 n,m

接下來 n行每行 m個整數,依次代表每個方格中的整數。

乙個整數,表示小熊能取到的整數之和的最大值。

輸入 #1

3 4

1 -1 3 2

2 -1 4 -1

-2 2 -3 -1

輸出 #1

9

輸入 #2

2 5

-1 -1 -3 -2 -7

-2 -1 -4 -1 -2

輸出 #2

-10

這個題看著很簡單,做起來好難啊qwq

一開始以為是dp,結果就是dp,但是還是不會做(╥╯^╰╥)

好啦,既然不會做,那麼就一起來學習一下正解的思路吧!!

首先,在開始之前還是先來講一講我自己的思路(當然這個是錯的)

我以為這道題就只是乙個簡單的dp而已,與其他dp不同的地方只是在於這個題目要求中,上下都可以走,也就是說小熊可以往復回環走,所以我們腦海中浮現的第一想法不就是:害,加乙個bool判斷一下這個格仔有沒有走過不就行了嗎?還用得著其他東西嗎?於是我這麼想的,也這麼做的,最後也這麼抱靈的~~

那麼問題來了,上面的方法為什麼是錯的呢?

我們這樣來想,我們如果在小熊走過的路上進行標記,那麼結果有兩證:

1.每經過乙個點標記一次小熊的路徑,但是很快我們發現這樣不行,如果這樣的話我們是按照一定的順序從上到下遍歷格仔來求每個格仔的最優解的,那麼如果這樣標記,我們最後的結果就只能是把所有的格仔都標記下來,那麼如果之後我們更新的時候發現還存在更優的解,那麼我們就不能再次更新了,因為這個點在之前已經被標記了,所以這種標記方法不可取

2.我們給這個點設定三個變數,分別表示其是否是從上下左三個方向轉移過來的,那麼如果是這樣的話,其實根本處境還是沒有什麼改變,如果存在一條更優的路徑,那麼因為之前我們已經把這個點進行了標記,這個點就不可以再走了,所以那一條更優路徑就會被我們捨棄。所以最後不一定可以找到最優解。

至此,我發現憑藉我的可憐巴巴的一點點的智商,可能沒有辦法利用標記法來完成這道題,所以我愉快(並不)的放棄了這道題:)

正解來啦||ヽ(* ̄▽ ̄*)ノミ|ю~~~

讓我來為你們詳細解釋一下正解~~

首先我們來觀察這個題與其他題的不同點在**。其他的dp題只能走上右或者下右,但是這個題卻可以走上下右,所以憑藉單純的dp肯定是無法解決這個問題的。

然後,對於每一列,由於題目規定我們「不能重複經過已經走過的方格」,所以我們在每一列上只能向上走或者向下走,而不能上下來回走,這樣就重複了。而只能走右不能走左,則表示對於每一列來說,當前一列的最大值一定是從左邊那一列推出來的,所以我們可以按照一列來進行劃分,把每一列當成乙個整體來求解。

1.只能向右走—>可以按照列來劃分,從左到右對每列進行求解

2.不能重複經過已經走過的方格—>每個方格只能向上或者向下走—>設定兩個變數來儲存從上面向下走的最優解和從下面向上走得最優解

這樣這個題的思路就很明白了,我們設定兩個陣列 up[i][j] 和 down[i][j] ,分別表示從下面走上來到 i 行 j 列的最大值和從上面走下來到 i 行 j 列的最大值。

當然最大值也是有可能從左邊來的,那麼我們還需不需要再開設乙個變數 left[i][j] 表示從左走到右的最大值呢?

答案是不需要的,因為從上文分析中我們可以得出結論,我們只能從左走到右,方向是唯一確定的,所以我們只需要用我們的 f[i][j] 總變數來表示第 i 行 j 列的點的最大值,而不需要再設定乙個變數表示從左走到右的最大值(當然你也可以理解為這兩個變數所表達的意思其實是相同的)。

那麼狀態轉移方程就可以寫出來了(a[i]][j] 表示 i 行 j 列的元素值)

up[i][j]=max(up[i+1][j],f[i][j-1])+a[i][j];

down[i][j]=max(down[i-1][j],f[i][j-1])+a[i][j];

f[i][j]=max(up[i][j],down[i][j]);

我們發現如果這樣設定狀態,這道題的確可以ac,但是我麼在前面的分析中其實還有一點沒有用到「可以把列當成乙個整體來處理」。

設定變數 up ,down ,f 分別為 up[i] , down[i] , f[i] ,分別表示這一行從下面走上來的最大值,這一行從上面走下來的最大值,這一行的最大值(注意,因為我們可以把列當做乙個整體,所以可以把一整列進行壓縮,但是行是不可以壓縮的,所以最後設定的一維變臉都是用來儲存行最優解的)。

那麼狀態轉移方程就變成了:(不行,我覺得下面的方程需要我來解釋一下了,畢竟當時我就是卡在這裡出不來的qwq)

up[i]=max(f[i],up[i+1])+a[i][j]; //這一行從下到上的最大值要麼是從下一行的up最大值轉移來的,要麼是從這一行的最大值( f[i] )轉移來的.為什麼還會從 f 轉移過來呢?因為我們上面就已經說過了,我們在這裡可以把 f 看做 left 變數,,所以當前最大值也可以從 f[i] 轉移過來。

down[i]=max(f[i],down[i-1])+a[i][j]; //同理,這一行從上到下的最大值要麼是從上一行down的最大值轉移過來的,要麼是從這一行的最大值 f[i] 轉移過來的。

f[i]=max(up[i],down[i]); //因為我們每更新完一次down 和 up ,這一行的最大值就有可能發生變化,所以 f 也應當更新,但是問題有來了,為什麼 f 不需要和自己進行比較一下呢?有沒有可能當前的 down 和 up 都沒有 f 自己的值大呢?答案是不可能的,因為down 和 up 就是從 f 轉移過來的,在轉移結束之前他們已經和 f 進行過比較,也就是說他們是和f 進行比較之後,又加上了當前方格中的元素,才轉移過來的,所以up 和down 的值一定比 f 大,所以我們只需要比較up 和down 的大小即可。

好了,囉裡囉嗦半天終於把所有事情都給解釋清楚了,但是**中還存在乙個令人迷惑的地方

/*

for(int j=2;j*/

看見變成綠色的部分了嗎?為什麼對於列的迴圈我們只對2到m-1進行更新,而不對1和m進行更新呢?我們來自習思考一下,其實對於第一列和最後一列來說,他們在我們心中都是特殊列。什麼叫特殊列呢?對於第一列,我們發現它不能從左邊更新到右邊,對於最後一列,我們發現它不能從下面更新到上面,所以我們需要對它們進行單獨處理,因為這樣幹講可能對於理解效果不太好,所以我們來借助ac**理解一下:

#include#include

#include

using

namespace

std;

intn,m;

int a[1001][1001

];long

long up[1005],down[1005],f[1005

];inline

intread()

while(ch>='

0'&&ch<='9')

return f*x;

}int

main()

f[1]+=a[1][m];//

處理一下細節

for(int i=2;i<=n;i++) f[i]=max(f[i],f[i-1])+a[i][m];//

然後問題又來了,最後一列我們還沒有處理呢,還記得f的定義嗎?

//走到當前格仔的最大值,而最後一列只有可能從上面或者從左面轉移過來,所以取從上面走下來,從左面走過來的最大值,加上

//當前格中元素的值就可以得到當前格仔中的最大值

printf("

%lld

",f[n]);//

輸出結果end~~ :)

return0;

}

---------end----------

題解 LuoGu7074 方格取數

原題傳送門 普及的題目就直接秒殺了 因為題目已經幫我們劃好了dp的階段,就是一列一列的走 然後在一列裡面可以分為向上走和向下走,那麼我需要知道上乙個階段每個地方的最優值,用dp陣列記錄下來,很套路得轉移就行了 code include define maxn 1010 define ll long ...

P1004 方格取數

這題有兩種做法。第一種是同時考慮兩個點,也就是用4個迴圈。列舉每個點的位置,並計算總分。f i j k l max f i j k l f i 1 j k 1 l f i j 1 k l 1 f i 1 j k l 1 f i j 1 k 1 l a i j a k l 當 i k j l 時,多算...

P1004 方格取數

題目描述 設有n n的方格圖 n 9 我們將其中的某些方格中填入正整數,而其他的方格中則放入數字0。如下圖所示 見樣例 a0 0 0 0 0 0 0 0 0 0 13 0 0 6 0 0 0 0 0 0 7 0 0 0 0 0 0 14 0 0 0 0 0 21 0 0 0 4 0 0 0 0 15...