剣はoffer100を指します:三角形の最小のパスの合計

質問:
三角形が与えられた場合、最小のトップダウンパスの合計を見つけます。
各ステップは、次の行の隣接するノードにのみ移動できます。ここで隣接するノードとは、添え字が前のレイヤーのノードの添え字と同じか、前のレイヤーのノードの添え字+1に等しい2つのノードを指します。つまり、現在の行のインデックスiにいる場合、次のステップは次の行のインデックスiまたはi+1に移動できます。
例1:
入力:triangle = [[2]、[3,4]、[6,5,7]、[4,1,8,3]]
出力:11
説明:次の図に示すように:
2
3 4
6 5 7
4 1 8 3
最小のトップダウンパスの合計は11です(つまり、2 + 3 + 5 + 1 = 11)。
例2:
入力:triangle = [[-10]]
出力:-10
分析:
状態遷移方程式を決定します。これは、三角形の上部から行番号と列番号に到達するf(i、j)で表すことができます。それぞれiとj(パス番号の合計の最小値)。T[i] [j]は、それぞれ三角形の行番号と列番号iとjを表します。三角形にn行の数値が含まれている場合、f(n-1、j)の最小値が問題全体の最適解です。

  • jが0に等しい場合、つまり行の最初の番号に等しい場合、パスの各ステップは真下または右下の番号に移動し、現在の位置の左上に番号がないためです。今回は、前のステップはそこから取得する必要があります。すぐ上の数値であるため、f(i、0)はf(i-1,0)とT[i][0]の合計に等しくなります。
  • iがjに等しい場合、つまり現在の行の最後の番号である場合、そのすぐ上に番号はなく、前のステップはその左上の番号からのみ取得できるため、f(i、j) f(i-1、j-1の合計)とT[i][j]に等しい。
  • 現在の行番号と列番号iおよびjが行の中央にある場合、前のステップはそのすぐ上の番号またはその上の左側の番号から来る可能性があるため、f(i、j)は等しくなります。 to f(i-1、j)とf(i-1、j-1)の最小値にT[i][j]を加えたもの。
    最初の方法では、n * n個の2次元配列dpを作成します。配列の左下部分のみを実際に使用する場合は、最初に二重ループ状態遷移方程式を使用してf(i、j)の値を計算し、に保存します。 dp [i] [j]次に、forループを使用して、問題全体の最適な解として2次元配列dpの最後の行の最小値を見つけます。2次元配列dpの左下部分の各数値は、1回計算する必要があるため、時間計算量はO(n ^ 2)です。入力三角形がArrayListで実装されていると仮定すると、毎回get関数を呼び出す時間計算量はO(1)です。
    スペース効率を最適化するには、1次元配列dpのみが必要です。f(i、j)、f(i-1、j)をdp [j]に保存できる場合は、1次元配列で必要なデータを保存できます。
    図に示すように、左から右に計算する場合:
    ここに画像の説明を挿入
    f(i、j)を計算した後、dp配列の最初の要素が置き換えられます。f(i、j + 1)を計算するときにf(i、j-1)が必要になる場合がありますが、f(i、j-1)は元のdp配列の最初の要素ですが、置き換えられているため、計算できません。左から右へ、ただし右から左へ計算できます。右から左へ計算する場合、最初にf(i、j)を計算します。これには、f(i-1、j-1)、f(i- 1、j)。次に、f(i、j-1)を計算するには、f(i-1、j-1)、f(i-1、j-2)を使用する必要があります。f(i、jを右から左に計算) )、f(i、j)の値をdp [j]に保存し、f(i-1、j)を置き換えても問題は発生しないため、f(i-1、j)の値は後でなくなります。必要です。
    コード:
import java.util.List;

public class MinimumTotal {
    
    
//
    public int minimumTotal1(List<List<Integer>> triangle) {
    
    
//        行数
        int size = triangle.size();
        int[][] dp = new int[size][size];
        for (int i = 0; i < size; i++) {
    
    
            for (int j = 0; j <= i; j++) {
    
    
                dp[i][j] = triangle.get(i).get(j);
//                如果得到的第一列下标为0,说明它的上一行元素下标也是0,因为下一行下标数字一定大于等于上一行下标数字
                if (i>0 && j==0){
    
    
                    dp[i][j] +=dp[i-1][j];
                }else if (i>0 && i==j){
    
    
//                    如果得到的行列数下标都相等,它的上一行元素一定也是对角线的元素,因为它上一行下标和它相等的不存在,只能找上一行
//                    同等下标左边那一个
                    dp[i][j] +=dp[i-1][j-1];
                }else if (i>0){
    
    
                    dp[i][j] +=Math.min(dp[i-1][j-1],dp[i-1][j]);
                }
            }
        }
        int min = Integer.MAX_VALUE;
//        找出二维数组dp最后一行的最小值作为整个问题的最优解
        for (int num:dp[size-1]){
    
    
            min = Math.min(min,num);
        }
        return min;
    }
    public int minimumTotal2(List<List<Integer>> triangle){
    
    
        int[] dp = new int[triangle.size()];
        for(List<Integer> row:triangle){
    
    
            for (int j = row.size()-1; j >=0; j--) {
    
    
                if (j==0){
    
    
                    dp[j]+=row.get(j);
                }else if (j == row.size()-1){
    
    
                    dp[j] = dp[j-1] + row.get(j);
                }else {
    
    
                    dp[j] = Math.min(dp[j],dp[j-1])+row.get(j);
                }
            }
        }
        int min = Integer.MAX_VALUE;
//        找出二维数组dp最后一行的最小值作为整个问题的最优解
        for (int num:dp){
    
    
            min = Math.min(min,num);
        }
        return min;
    }
}

ここに画像の説明を挿入
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/Jiaodaqiaobiluo/article/details/123226966