/*动态规划  矩阵大小MxN
    遍历了,时间复杂度O(MxN)
    dp大小int [M][N],空间复杂度O(MxN)
    * */
    public static int minPathSum1(int[][]m){
        if(m==null||m.length==0||m[0].length==0||m[0]==null){
            return 0;
        }
        int row=m.length;       //行
        int col=m[0].length;    //列
        int[][]dp=new int[row][col];

        //起点
        dp[0][0]=m[0][0];
        //第一行
        for (int i = 1; i <col ; i++) {
            dp[0][i]=dp[0][i-1]+m[0][i];
        }
        //第一列
        for (int i = 1; i <row ; i++) {
            dp[i][0]=dp[i-1][0]+m[i][0];
        }

        for (int i = 1; i <row ; i++) {
            for (int j = 1; j <col ; j++) {
                dp[i][j]=Math.min(dp[i-1][j], dp[i][j-1])+m[i][j];
            }
        }

        return dp[row-1][col-1];
    }


    //时间复杂度不变,空间复杂度O(min{M,N})
    public static int minPathSum2(int[][]m) {
        if (m == null || m.length == 0 || m[0].length == 0 || m[0] == null) {
            return 0;
        }
        int more=Math.max(m.length,m[0].length);    //行数与列数中较大的
        int less=Math.min(m.length,m[0].length);    //行数与列数中较小的
        boolean rowmore= m.length==more;        //true:行数较大
        int[]arr=new int[less];
        arr[0]=m[0][0];
        for (int i = 1; i < less; i++) {
            arr[i]=arr[i-1]+(rowmore?m[0][i]:m[i][0]);
        }

        for (int i = 1; i <more ; i++) {
            //换行更新
            arr[0]=arr[0]+(rowmore?m[i][0]:m[0][i]);
            for (int j = 1; j <less ; j++) {
                arr[j]= Math.min(arr[j-1],arr[j]) + (rowmore?m[i][j]:m[j][i]);
            }
        }

        return arr[less-1];
    }


    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[][] arr = new int[n][m];
        for(int i=0;i<n;i++){
            for(int j = 0;j<m;j++){
                arr[i][j] = sc.nextInt();
            }
        }
        System.out.println(minPathSum1(arr));
    }