藍橋杯--傳紙條

藍橋杯問題–傳紙條(動態規劃)

題目描述

小淵和小軒是好朋友也是同班同學,他們在一起 總有談不完的話題。一次素質拓展活動中,班上同學安排做成一個m行n列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,因此,他們就無法直接交談了。幸運 的是,他們可以通過傳紙條來進行交流。紙條要經由許多同學傳到對方手裏,小淵坐在矩陣的左上角,座標(1,1),小軒坐在矩陣的右下角,座標(m,n)。 從小淵傳到小軒的紙條只可以向下或者向右傳遞,從小軒傳給小淵的紙條只可以向上或者向左傳遞。
在活動進行中,小淵希望給小軒傳遞一張紙條,同時希望小軒給他回覆。班裏每個同學都可以幫他們傳遞,但只會幫他們一次,也就是說如果此人在小淵遞給小軒紙條的時候幫忙,那麼在小軒遞給小淵的時候就不會再幫忙。反之亦然。
還有一件事情需要注意,全班每個同學願意幫忙的好感度有高有低(注意:小淵和小軒的好心程度沒有定義,輸入時用0表示),可以用一個0-100的自然 數來表示,數越大表示越好心。小淵和小軒希望儘可能找好心程度高的同學來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同學的好心程度只和最大。 現在,請你幫助小淵和小軒找到這樣的兩條路徑。

輸入描述

輸入第一行有2個用空格隔開的整數m和n,表示班裏有m行n列(1<=m,n<=50)。
接下來的m行是一個m*n的矩陣,矩陣中第i行j列的整數表示坐在第i行j列的學生的好心程度。每行的n個整數之間用空格隔開。

輸出描述

輸出一行,包含一個整數,表示來回兩條路上參與傳遞紙條的學生的好心程度之和的最大值。

樣例輸入

3 3
0 3 9
2 8 5
5 7 0

樣例輸出

34

數據規模和約定

30%的數據滿足:1<=m,n<=10
100%的數據滿足:1<=m,n<=50

解題

解題思路

本題進行分析之後,我們知道,我們就是要尋找一條好心值最大的往返路線。
如此一來,不如我們先看另一道題,也就是我們只找到去的好心值最大的路線,而不去尋找回來的時候的好心值最大的路線。
這樣子一來,我們很輕鬆的可以列出動態規劃方程

dp[x][y] = max(dp[x-1][y],dp[x][y-1]) + map[x][y];

這樣子,我們可以很好的列出動態規劃方程。因爲我只能向上或者向下去走,所以說,我走到這一點時候的好心值最大的時候就是我上一點所走的好心值最大的時候加上我這點的好心值即可。這就是我這點的最大好心值。上一點,由於只可以向上和向下走,所以說,上一點就是我左邊的點或者上面的點。這就是可以列出我們的動態規劃方程。

這樣一來,我們來思考我們如果去寫這一題。本題難就難在我不僅要知道去的時候的好心值最大的時候,也需要求出回來的時候好心值最大的時候。
在開始的時候,我進行計算,發現我無法利用普通的方法去算出結果,也就是按照先找出去的路徑再找出回來的路徑,我無法計算,這個時候求助了網上的度娘,我知道了另一種寫法。
我們可以不去按照題目的意思進行計算,我們可以換個思路去進行思考該問題,也就是說,我們爲什麼要考慮往返呢?只要兩條不相同的路徑即可,最後要求這兩條路徑上的好心值和是最大的即可,那能不能一開始的時候傳兩張紙條呢?要求這兩張紙條去走不相同的路線即可。 這樣傳遞的兩張紙條所走的路徑其實實際上和題目所要求的走的路徑是一樣一樣的。

題目的思路進行確定,現在我們可以思考解題,這道題目還有一個重要的思想,就是階段性。也就是說,我們走的時候,第一張紙條走一步,第二張紙條也要走一步,不能說,第一張紙條走到終點的時候,一回頭看,發現第二張紙條還在起點的位置睡覺呢。所以說,一定要考慮到一起走的問題。

我們在解決一張紙條傳送到終點的時候,使用二維數組去記錄,那麼相應的,我們兩張紙條去走的話,使用者四維數組去記錄。
我們知道有着四種情況:
1.第一張紙條從上面走來,第二張紙條從上面走來;
2.第一張紙條從上面走來,第二張紙條從左面走來;
3.第一張紙條從左面走來,第二張紙條從上面走來;
4.第一張紙條從左面走來,第二張紙條從左面走來;
這樣子,可以列出動態規劃方程

int tmp1 = max(dp[x1-1][y1][x2-1][y2],dp[x1-1][y1][x2][y2-1]);
int tmp2 = max(dp[x1][y1-1][x2-1][y2],dp[x1][y1-1][x2][y2-1]);
int maxHao = max(tmp1,tmp2);

計算出來,他們上一步的時候最大的好心值是多少,然後我們再將他的好心值加上這兩張紙條走到的位置的時候的好心值,就是我們走到這一點的最大好心值.

dp[x1][y1][x2][y2] = maxHao + map[x1][y1] + max[x2][y2];

這個時候我們加上了這一點的好心值,但是如果我們將這一點計算了兩次的話我們豈不是重複計算了嗎?沒有關係,我們可以進行判斷,判斷是否重複計算,是的話,我們只需要減去這一點的好心值即可,有的讀者問,我們減去了這一點的好心值的話,豈不是我們就默認了重複走的路徑了嗎?其實不然,因爲你想一想,兩張紙條我走了兩個階段的話,我們本來可以加上四個格子的好心值,現在就編程了三個格子的好心值,那麼是不是三個格子一定比不上四個格子的?所以,下一步計算好心值的時候,這三個格子的,還會是最大的好心值嗎?不會,所以計算下一個格子的時候我也不會加上你了。

if(x1 == x2 && y1 == y2){
  dp[x1][y1][x2][y2] -= a[x1][y2];
}

這是判斷重複的,並減去它,其實我也可以開始的時候只加上一個格子的好心值,然後發現兩個座標不相等的時候,我再度加上另一個紙條的走到位置的好心值。我會在下面主代碼中使用該方法。

#include <iostream>
#include <algorithm>
using namespace std;
int dp[55][55][55][55];
int a[55][55];//地圖
int n, m;//行,高
int main()
{
    //輸入
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> a[i][j];
    
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            for(int l = 1; l <= n; l++)
                for(int k = 1; k <= m; k++)
                {
                    int tmp1 = max(dp[i-1][j][l-1][k],dp[i-1][j][l][k-1]);
                    int tmp2 = max(dp[i][j-1][l-1][k],dp[i][j-1][l][k-1]);
                    int maxHao = max(tmp1,tmp2);
                    dp[i][j][l][k] = maxHao + a[i][j];
                    if(i!=l || j!=k)//發現他們的座標不同的時候,加上另一張紙條所處位置的好心值,相同,計算一個即可,防止重複
                        dp[i][j][l][k] += a[l][k];
                }
    cout << dp[n][m][n][m] << endl;//最後輸出,兩張紙條到達最後地圖的位置的時候的好心值即可
    return 0;
}

其實,本題還可以進行優化,優化成爲三維數組的問題,這裏就不進行敘說。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章